history.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "log"
  6. "os"
  7. "strconv"
  8. "time"
  9. "github.com/bwmarrin/discordgo"
  10. "github.com/fatih/color"
  11. "github.com/hako/durafmt"
  12. )
  13. var (
  14. historyStatus map[string]string
  15. )
  16. func handleHistory(commandingMessage *discordgo.Message, subjectChannelID string, before string, since string) int {
  17. // Identifier
  18. var commander string = "AUTORUN"
  19. if commandingMessage != nil {
  20. commander = getUserIdentifier(*commandingMessage.Author)
  21. }
  22. logPrefix := fmt.Sprintf("%s/%s: ", subjectChannelID, commander)
  23. // Check Read History perms
  24. if !hasPerms(subjectChannelID, discordgo.PermissionReadMessageHistory) {
  25. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"BOT DOES NOT HAVE PERMISSION TO READ MESSAGE HISTORY!!!"))
  26. return 0
  27. }
  28. // Mark active
  29. historyStatus[subjectChannelID] = "downloading"
  30. var i int64 = 0
  31. var d int64 = 0
  32. var batch int = 0
  33. var beforeID string
  34. if before != "" {
  35. beforeID = before
  36. }
  37. var beforeTime time.Time
  38. var sinceID string
  39. if since != "" {
  40. sinceID = since
  41. }
  42. rangeContent := ""
  43. if since != "" {
  44. if isDate(since) {
  45. rangeContent += fmt.Sprintf("**Since:** `%s`\n", discordSnowflakeToTimestamp(since, "2006-01-02"))
  46. } else if isNumeric(since) {
  47. rangeContent += fmt.Sprintf("**Since:** `%s`\n", since)
  48. }
  49. }
  50. if before != "" {
  51. if isDate(before) {
  52. rangeContent += fmt.Sprintf("**Before:** `%s`", discordSnowflakeToTimestamp(before, "2006-01-02"))
  53. } else if isNumeric(before) {
  54. rangeContent += fmt.Sprintf("**Before:** `%s`\n", before)
  55. }
  56. }
  57. if rangeContent != "" {
  58. rangeContent += "\n\n"
  59. }
  60. var err error
  61. var message *discordgo.Message = nil
  62. if isChannelRegistered(subjectChannelID) {
  63. channelConfig := getChannelConfig(subjectChannelID)
  64. // Open Cache File?
  65. if historyCachePath != "" {
  66. filepath := historyCachePath + string(os.PathSeparator) + subjectChannelID
  67. if f, err := ioutil.ReadFile(filepath); err == nil {
  68. beforeID = string(f)
  69. if commandingMessage != nil && config.DebugOutput {
  70. log.Println(logPrefixDebug, color.YellowString(logPrefix+"Found a cache file, picking up where we left off...", subjectChannelID, commander))
  71. }
  72. }
  73. }
  74. historyStartTime := time.Now()
  75. // Initial Status Message
  76. if commandingMessage != nil {
  77. if hasPerms(commandingMessage.ChannelID, discordgo.PermissionSendMessages) {
  78. message, err = replyEmbed(commandingMessage, "Command — History", "Starting to save channel history, please wait...")
  79. if err != nil {
  80. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Failed to send command embed message:\t%s", err))
  81. }
  82. } else {
  83. log.Println(logPrefixHistory, color.HiRedString(logPrefix+fmtBotSendPerm, commandingMessage.ChannelID))
  84. }
  85. }
  86. log.Println(logPrefixHistory, color.CyanString(logPrefix+"Began checking history for %s...", subjectChannelID))
  87. MessageRequestingLoop:
  88. for true {
  89. // Next 100
  90. if beforeTime != (time.Time{}) {
  91. batch++
  92. // Write to cache file
  93. if historyCachePath != "" {
  94. err := os.MkdirAll(historyCachePath, 0755)
  95. if err != nil {
  96. log.Println(logPrefixHistory, color.HiRedString("Error while creating history cache folder \"%s\": %s", historyCachePath, err))
  97. }
  98. filepath := historyCachePath + string(os.PathSeparator) + subjectChannelID
  99. f, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  100. if err != nil {
  101. log.Println(logPrefixHistory, color.RedString("Failed to open cache file:\t%s", err))
  102. }
  103. if _, err = f.WriteString(beforeID); err != nil {
  104. log.Println(logPrefixHistory, color.RedString("Failed to write cache file:\t%s", err))
  105. } else if commandingMessage != nil && config.DebugOutput {
  106. log.Println(logPrefixDebug, logPrefixHistory, color.YellowString(logPrefix+"Wrote to cache file."))
  107. }
  108. f.Close()
  109. }
  110. // Status Update
  111. if commandingMessage != nil {
  112. log.Println(logPrefixHistory, color.CyanString(logPrefix+"Requesting 100 more, %d downloaded, %d processed — Before %s",
  113. d, i, beforeTime))
  114. if message != nil {
  115. if hasPerms(message.ChannelID, discordgo.PermissionSendMessages) {
  116. content := fmt.Sprintf("``%s:`` **%s files downloaded**\n``%s messages processed``\n\n%s`(%d)` _Processing more messages, please wait..._",
  117. durafmt.ParseShort(time.Since(historyStartTime)).String(),
  118. formatNumber(d), formatNumber(i), rangeContent, batch)
  119. message, err = bot.ChannelMessageEditComplex(&discordgo.MessageEdit{
  120. ID: message.ID,
  121. Channel: message.ChannelID,
  122. Embed: buildEmbed(message.ChannelID, "Command — History", content),
  123. })
  124. // Edit failure, so send replacement status
  125. if err != nil {
  126. log.Println(logPrefixHistory, color.RedString(logPrefix+"Failed to edit status message, sending new one:\t%s", err))
  127. message, err = replyEmbed(message, "Command — History", content)
  128. if err != nil {
  129. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Failed to send replacement status message:\t%s", err))
  130. }
  131. }
  132. } else {
  133. log.Println(logPrefixHistory, color.HiRedString(logPrefix+fmtBotSendPerm, message.ChannelID))
  134. }
  135. } else {
  136. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Tried to edit status message but it doesn't exist.", subjectChannelID, commander))
  137. }
  138. }
  139. // Update presence
  140. timeLastUpdated = time.Now()
  141. if *channelConfig.UpdatePresence {
  142. updateDiscordPresence()
  143. }
  144. }
  145. // Request More
  146. messages, err := bot.ChannelMessages(subjectChannelID, 100, beforeID, sinceID, "")
  147. if err == nil {
  148. // No More Messages
  149. if len(messages) <= 0 {
  150. delete(historyStatus, subjectChannelID)
  151. break MessageRequestingLoop
  152. }
  153. // Go Back
  154. beforeID = messages[len(messages)-1].ID
  155. beforeTime, err = messages[len(messages)-1].Timestamp.Parse()
  156. if err != nil {
  157. log.Println(logPrefixHistory, color.RedString(logPrefix+"Failed to fetch message timestamp:\t%s", err))
  158. }
  159. sinceID = ""
  160. // Process Messages
  161. if *channelConfig.TypeWhileProcessing && hasPerms(commandingMessage.ChannelID, discordgo.PermissionSendMessages) {
  162. bot.ChannelTyping(commandingMessage.ChannelID)
  163. }
  164. for _, message := range messages {
  165. // Ordered to Cancel
  166. if historyStatus[message.ChannelID] == "cancel" {
  167. delete(historyStatus, message.ChannelID)
  168. break MessageRequestingLoop
  169. }
  170. // Check Before/Since
  171. message_int64, _ := strconv.ParseInt(message.ID, 10, 64)
  172. if before != "" {
  173. before_int64, _ := strconv.ParseInt(before, 10, 64)
  174. if message_int64 > before_int64 {
  175. continue
  176. }
  177. }
  178. if since != "" {
  179. since_int64, _ := strconv.ParseInt(since, 10, 64)
  180. if message_int64 < since_int64 {
  181. continue
  182. }
  183. }
  184. // Process
  185. downloadCount := handleMessage(message, false, true)
  186. if downloadCount > 0 {
  187. d += downloadCount
  188. }
  189. i++
  190. }
  191. } else {
  192. // Error requesting messages
  193. if message != nil {
  194. if hasPerms(message.ChannelID, discordgo.PermissionSendMessages) {
  195. _, err = replyEmbed(message, "Command — History", fmt.Sprintf("Encountered an error requesting messages: %s", err.Error()))
  196. if err != nil {
  197. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Failed to send error message:\t%s", err))
  198. }
  199. } else {
  200. log.Println(logPrefixHistory, color.HiRedString(logPrefix+fmtBotSendPerm, message.ChannelID))
  201. }
  202. }
  203. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Error requesting messages:\t%s", err))
  204. delete(historyStatus, subjectChannelID)
  205. break MessageRequestingLoop
  206. }
  207. }
  208. // Final status update
  209. if commandingMessage != nil {
  210. if message != nil {
  211. if hasPerms(message.ChannelID, discordgo.PermissionSendMessages) {
  212. contentFinal := fmt.Sprintf("``%s:`` **%s total files downloaded!**\n``%s total messages processed``\n\nFinished cataloging history for ``%s``\n``%d`` message history requests\n\n%s_Duration was %s_",
  213. durafmt.ParseShort(time.Since(historyStartTime)).String(),
  214. formatNumber(int64(d)), formatNumber(int64(i)),
  215. subjectChannelID, batch,
  216. rangeContent,
  217. durafmt.Parse(time.Since(historyStartTime)).String(),
  218. )
  219. message, err = bot.ChannelMessageEditComplex(&discordgo.MessageEdit{
  220. ID: message.ID,
  221. Channel: message.ChannelID,
  222. Embed: buildEmbed(message.ChannelID, "Command — History", contentFinal),
  223. })
  224. // Edit failure
  225. if err != nil {
  226. log.Println(logPrefixHistory, color.RedString(logPrefix+"Failed to edit status message, sending new one:\t%s", err))
  227. message, err = replyEmbed(message, "Command — History", contentFinal)
  228. if err != nil {
  229. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Failed to send replacement status message:\t%s", err))
  230. }
  231. }
  232. } else {
  233. log.Println(logPrefixHistory, color.HiRedString(logPrefix+fmtBotSendPerm, message.ChannelID))
  234. }
  235. } else {
  236. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Tried to edit status message but it doesn't exist.", subjectChannelID, commander))
  237. }
  238. }
  239. // Final log
  240. log.Println(logPrefixHistory, color.HiCyanString(logPrefix+"Finished history, %s files", formatNumber(d)))
  241. // Delete Cache File
  242. if historyCachePath != "" {
  243. filepath := historyCachePath + string(os.PathSeparator) + subjectChannelID
  244. if _, err := os.Stat(filepath); err == nil {
  245. err = os.Remove(filepath)
  246. if err != nil {
  247. log.Println(logPrefixHistory, color.HiRedString(logPrefix+"Encountered error deleting cache file:\t%s", err))
  248. } else if commandingMessage != nil && config.DebugOutput {
  249. log.Println(logPrefixDebug, logPrefixHistory, color.YellowString(logPrefix+"Deleted cache file."))
  250. }
  251. }
  252. }
  253. }
  254. return int(d)
  255. }