handlers.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "strings"
  7. "time"
  8. "github.com/bwmarrin/discordgo"
  9. "github.com/fatih/color"
  10. )
  11. type fileItem struct {
  12. Link string
  13. Filename string
  14. Time time.Time
  15. }
  16. //#region Events
  17. var lastMessageID string
  18. func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
  19. if lastMessageID != m.ID {
  20. handleMessage(m.Message, nil, false, false)
  21. }
  22. lastMessageID = m.ID
  23. }
  24. func messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
  25. if lastMessageID != m.ID {
  26. if m.EditedTimestamp != nil {
  27. handleMessage(m.Message, nil, true, false)
  28. }
  29. }
  30. lastMessageID = m.ID
  31. }
  32. func handleMessage(m *discordgo.Message, c *discordgo.Channel, edited bool, history bool) (int64, int64) {
  33. // Ignore own messages unless told not to
  34. if m.Author.ID == botUser.ID && !config.ScanOwnMessages {
  35. return -1, 0
  36. }
  37. if !history && !edited {
  38. timeLastMessage = time.Now()
  39. }
  40. // Admin Channel
  41. if isAdminChannelRegistered(m.ChannelID) {
  42. m = fixMessage(m)
  43. // Log
  44. sendLabel := fmt.Sprintf("%s in \"%s\"#%s",
  45. getUserIdentifier(*m.Author),
  46. getGuildName(m.GuildID), getChannelName(m.ChannelID, nil),
  47. )
  48. content := m.Content
  49. if len(m.Attachments) > 0 {
  50. content = content + fmt.Sprintf(" (%d attachments)", len(m.Attachments))
  51. }
  52. if edited {
  53. log.Println(lg("Message", "ADMIN CHANNEL", color.CyanString, "Edited [%s]: %s", sendLabel, content))
  54. } else {
  55. log.Println(lg("Message", "ADMIN CHANNEL", color.CyanString, "[%s]: %s", sendLabel, content))
  56. }
  57. }
  58. // Registered Channel
  59. if channelConfig := getSource(m, c); channelConfig != emptyConfig {
  60. // Ignore bots if told to do so
  61. if m.Author.Bot && *channelConfig.IgnoreBots {
  62. return -1, 0
  63. }
  64. // Ignore if told so by config
  65. if (!history && !*channelConfig.Enabled) || (edited && !*channelConfig.ScanEdits) {
  66. return -1, 0
  67. }
  68. m = fixMessage(m)
  69. // Log
  70. if config.MessageOutput {
  71. sendLabel := fmt.Sprintf("%s in \"%s\"#%s",
  72. getUserIdentifier(*m.Author),
  73. getGuildName(m.GuildID), getChannelName(m.ChannelID, nil),
  74. )
  75. content := m.Content
  76. if len(m.Attachments) > 0 {
  77. content += fmt.Sprintf(" \t[%d attachments]", len(m.Attachments))
  78. }
  79. content += fmt.Sprintf(" \t<%s>", m.ID)
  80. if !history || config.MessageOutputHistory {
  81. addOut := ""
  82. if history && config.MessageOutputHistory && !m.Timestamp.IsZero() {
  83. addOut = fmt.Sprintf(" @ %s", m.Timestamp.String()[:19])
  84. }
  85. if edited {
  86. log.Println(lg("Message", "", color.CyanString, "Edited [%s%s]: %s", sendLabel, addOut, content))
  87. } else {
  88. log.Println(lg("Message", "", color.CyanString, "[%s%s]: %s", sendLabel, addOut, content))
  89. }
  90. }
  91. }
  92. // Log Messages to File
  93. if channelConfig.LogMessages != nil {
  94. if channelConfig.LogMessages.Destination != "" {
  95. logPath := channelConfig.LogMessages.Destination
  96. if *channelConfig.LogMessages.DestinationIsFolder {
  97. if !strings.HasSuffix(logPath, string(os.PathSeparator)) {
  98. logPath += string(os.PathSeparator)
  99. }
  100. err := os.MkdirAll(logPath, 0755)
  101. if err == nil {
  102. logPath += "Log_Messages"
  103. if *channelConfig.LogMessages.DivideLogsByServer {
  104. if m.GuildID == "" {
  105. ch, err := bot.State.Channel(m.ChannelID)
  106. if err != nil && c != nil {
  107. ch = c
  108. }
  109. if ch != nil {
  110. if ch.Type == discordgo.ChannelTypeDM {
  111. logPath += " DM"
  112. } else if ch.Type == discordgo.ChannelTypeGroupDM {
  113. logPath += " GroupDM"
  114. } else if ch.Type == discordgo.ChannelTypeGuildText {
  115. logPath += " Text"
  116. } else if ch.Type == discordgo.ChannelTypeGuildCategory {
  117. logPath += " Category"
  118. } else if ch.Type == discordgo.ChannelTypeGuildForum {
  119. logPath += " Forum"
  120. } else if ch.Type == discordgo.ChannelTypeGuildPrivateThread || ch.Type == discordgo.ChannelTypeGuildPublicThread {
  121. logPath += " Thread"
  122. } else {
  123. logPath += " Unknown"
  124. }
  125. if ch.Name != "" {
  126. logPath += " - " + clearPath(ch.Name) + " -"
  127. } else if ch.Topic != "" {
  128. logPath += " - " + clearPath(ch.Topic) + " -"
  129. }
  130. }
  131. } else {
  132. logPath += " SID_" + m.GuildID
  133. }
  134. }
  135. if *channelConfig.LogMessages.DivideLogsByChannel {
  136. logPath += " CID_" + m.ChannelID
  137. }
  138. if *channelConfig.LogMessages.DivideLogsByUser {
  139. logPath += " UID_" + m.Author.ID
  140. }
  141. }
  142. logPath += ".txt"
  143. }
  144. // Read
  145. currentLog, err := os.ReadFile(logPath)
  146. currentLogS := ""
  147. if err == nil {
  148. currentLogS = string(currentLog)
  149. }
  150. canLog := true
  151. // Filter Duplicates
  152. if channelConfig.LogMessages.FilterDuplicates != nil {
  153. if *channelConfig.LogMessages.FilterDuplicates {
  154. if strings.Contains(currentLogS, fmt.Sprintf("[%s/%s/%s]", m.GuildID, m.ChannelID, m.ID)) {
  155. canLog = false
  156. }
  157. }
  158. }
  159. if canLog {
  160. // Writer
  161. f, err := os.OpenFile(logPath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0600)
  162. if err != nil {
  163. log.Println(lg("Message", "", color.RedString, "[channelConfig.LogMessages] Failed to open log file:\t%s", err))
  164. f.Close()
  165. }
  166. defer f.Close()
  167. var newLine string
  168. // Prepend
  169. prefix := ""
  170. if channelConfig.LogMessages.Prefix != nil {
  171. prefix = *channelConfig.LogMessages.Prefix
  172. }
  173. // More Data
  174. additionalInfo := ""
  175. if channelConfig.LogMessages.UserData != nil {
  176. if *channelConfig.LogMessages.UserData {
  177. additionalInfo = fmt.Sprintf("[%s/%s/%s] \"%s\"#%s (%s) @ %s: ", m.GuildID, m.ChannelID, m.ID, m.Author.Username, m.Author.Discriminator, m.Author.ID, m.Timestamp)
  178. }
  179. }
  180. if len(m.Attachments) > 0 {
  181. additionalInfo += fmt.Sprintf("<%d ATTACHMENTS> ", len(m.Attachments))
  182. }
  183. // Append
  184. suffix := ""
  185. if channelConfig.LogMessages.Suffix != nil {
  186. suffix = *channelConfig.LogMessages.Suffix
  187. }
  188. // New Line
  189. contentFmt, err := m.ContentWithMoreMentionsReplaced(bot)
  190. if err == nil {
  191. newLine += "\n" + prefix + additionalInfo + contentFmt + suffix
  192. } else {
  193. newLine += "\n" + prefix + additionalInfo + m.Content + suffix
  194. }
  195. if _, err = f.WriteString(newLine); err != nil {
  196. log.Println(lg("Message", "", color.RedString, "[channelConfig.LogMessages] Failed to append file:\t%s", err))
  197. }
  198. }
  199. }
  200. }
  201. // Filters
  202. if channelConfig.Filters != nil {
  203. shouldAbort := false
  204. if channelConfig.Filters.AllowedPhrases != nil ||
  205. channelConfig.Filters.AllowedUsers != nil ||
  206. channelConfig.Filters.AllowedRoles != nil {
  207. shouldAbort = true
  208. if config.Debug {
  209. log.Println(lg("Debug", "Message", color.YellowString,
  210. "%s Filter will be ignoring by default...",
  211. color.HiMagentaString("(FILTER)")))
  212. }
  213. }
  214. if channelConfig.Filters.BlockedPhrases != nil {
  215. for _, phrase := range *channelConfig.Filters.BlockedPhrases {
  216. if strings.Contains(m.Content, phrase) {
  217. shouldAbort = true
  218. if config.Debug {
  219. log.Println(lg("Debug", "Message", color.YellowString,
  220. "%s blockedPhrases found \"%s\" in message, planning to abort...",
  221. color.HiMagentaString("(FILTER)"), phrase))
  222. }
  223. break
  224. }
  225. }
  226. }
  227. if channelConfig.Filters.AllowedPhrases != nil {
  228. for _, phrase := range *channelConfig.Filters.AllowedPhrases {
  229. if strings.Contains(m.Content, phrase) {
  230. shouldAbort = false
  231. if config.Debug {
  232. log.Println(lg("Debug", "Message", color.YellowString,
  233. "%s allowedPhrases found \"%s\" in message, planning to process...",
  234. color.HiMagentaString("(FILTER)"), phrase))
  235. }
  236. break
  237. }
  238. }
  239. }
  240. if channelConfig.Filters.BlockedUsers != nil {
  241. if stringInSlice(m.Author.ID, *channelConfig.Filters.BlockedUsers) {
  242. shouldAbort = true
  243. if config.Debug {
  244. log.Println(lg("Debug", "Message", color.YellowString,
  245. "%s blockedUsers caught %s, planning to abort...",
  246. color.HiMagentaString("(FILTER)"), m.Author.ID))
  247. }
  248. }
  249. }
  250. if channelConfig.Filters.AllowedUsers != nil {
  251. if stringInSlice(m.Author.ID, *channelConfig.Filters.AllowedUsers) {
  252. shouldAbort = false
  253. if config.Debug {
  254. log.Println(lg("Debug", "Message", color.YellowString,
  255. "%s allowedUsers caught %s, planning to process...",
  256. color.HiMagentaString("(FILTER)"), m.Author.ID))
  257. }
  258. }
  259. }
  260. if channelConfig.Filters.BlockedRoles != nil {
  261. member := m.Member
  262. if member == nil {
  263. member, _ = bot.GuildMember(m.GuildID, m.Author.ID)
  264. }
  265. if member != nil {
  266. for _, role := range member.Roles {
  267. if stringInSlice(role, *channelConfig.Filters.BlockedRoles) {
  268. shouldAbort = true
  269. if config.Debug {
  270. log.Println(lg("Debug", "Message", color.YellowString,
  271. "%s blockedRoles caught %s, planning to abort...",
  272. color.HiMagentaString("(FILTER)"), role))
  273. }
  274. break
  275. }
  276. }
  277. }
  278. }
  279. if channelConfig.Filters.AllowedRoles != nil {
  280. member := m.Member
  281. if member == nil {
  282. member, _ = bot.GuildMember(m.GuildID, m.Author.ID)
  283. }
  284. if member != nil {
  285. for _, role := range member.Roles {
  286. if stringInSlice(role, *channelConfig.Filters.AllowedRoles) {
  287. shouldAbort = false
  288. if config.Debug {
  289. log.Println(lg("Debug", "Message", color.YellowString,
  290. "%s allowedRoles caught %s, planning to allow...",
  291. color.HiMagentaString("(FILTER)"), role))
  292. }
  293. break
  294. }
  295. }
  296. }
  297. }
  298. // Abort
  299. if shouldAbort {
  300. if config.Debug {
  301. log.Println(lg("Debug", "Message", color.YellowString,
  302. "%s Filter decided to ignore message...",
  303. color.HiMagentaString("(FILTER)")))
  304. }
  305. return -1, 0
  306. }
  307. }
  308. // Process Files
  309. var downloadCount int64 = 0
  310. var totalfilesize int64 = 0
  311. files := getFileLinks(m)
  312. for _, file := range files {
  313. if file.Link == "" {
  314. continue
  315. }
  316. if config.Debug && (!history || config.MessageOutputHistory) {
  317. log.Println(lg("Debug", "Message", color.HiCyanString, "FOUND FILE: "+file.Link+fmt.Sprintf(" \t<%s>", m.ID)))
  318. }
  319. status, filesize := downloadRequestStruct{
  320. InputURL: file.Link,
  321. Filename: file.Filename,
  322. Path: channelConfig.Destination,
  323. Message: m,
  324. FileTime: file.Time,
  325. HistoryCmd: history,
  326. EmojiCmd: false,
  327. StartTime: time.Now(),
  328. }.handleDownload()
  329. if status.Status == downloadSuccess {
  330. downloadCount++
  331. totalfilesize += filesize
  332. }
  333. }
  334. return downloadCount, totalfilesize
  335. }
  336. return -1, 0
  337. }
  338. //#endregion