config.go 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438
  1. package main
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "log"
  6. "os"
  7. "runtime"
  8. "strings"
  9. "github.com/bwmarrin/discordgo"
  10. "github.com/fatih/color"
  11. "github.com/muhammadmuzzammil1998/jsonc"
  12. "gopkg.in/ini.v1"
  13. )
  14. var (
  15. config = defaultConfiguration()
  16. )
  17. //#region Config, Credentials
  18. var (
  19. placeholderToken string = "REPLACE_WITH_YOUR_TOKEN_OR_DELETE_LINE"
  20. placeholderEmail string = "REPLACE_WITH_YOUR_EMAIL_OR_DELETE_LINE"
  21. placeholderPassword string = "REPLACE_WITH_YOUR_PASSWORD_OR_DELETE_LINE"
  22. )
  23. type configurationCredentials struct {
  24. // Login
  25. Token string `json:"token,omitempty"` // required for bot token (this or login)
  26. Email string `json:"email,omitempty"` // required for login (this or token)
  27. Password string `json:"password,omitempty"` // required for login (this or token)
  28. // APIs
  29. TwitterUsername string `json:"twitterUsername,omitempty"` // optional
  30. TwitterPassword string `json:"twitterPassword,omitempty"` // optional
  31. InstagramUsername string `json:"instagramUsername,omitempty"` // optional
  32. InstagramPassword string `json:"instagramPassword,omitempty"` // optional
  33. FlickrApiKey string `json:"flickrApiKey,omitempty"` // optional
  34. }
  35. //#endregion
  36. //#region Config, Main
  37. // defConfig_ = Config Default
  38. // Needed for settings used without redundant nil checks, and settings defaulting + creation
  39. var (
  40. defConfig_Debug bool = false
  41. defConfig_CommandPrefix string = "ddg "
  42. defConfig_ScanOwnMessages bool = false
  43. defConfig_GithubUpdateChecking bool = true
  44. // Appearance
  45. defConfig_PresenceEnabled bool = true
  46. defConfig_PresenceStatus string = string(discordgo.StatusIdle)
  47. defConfig_PresenceType discordgo.GameType = discordgo.GameTypeGame
  48. defConfig_ReactWhenDownloaded bool = false
  49. defConfig_InflateDownloadCount int64 = 0
  50. // These are only defaults to "fix" when loading settings for when people put stupid values
  51. defConfig_ProcessLimit int = 32
  52. defConfig_DiscordTimeout int = 180
  53. defConfig_DownloadTimeout int = 60
  54. defConfig_DownloadRetryMax int = 3
  55. defConfig_CheckupRate int = 30
  56. defConfig_ConnectionCheckRate int = 5
  57. defConfig_PresenceRefreshRate int = 3
  58. defConfig_FilenameDateFormat string = "2006-01-02_15-04-05"
  59. defConfig_FilenameFormat string = "{{date}} {{file}}"
  60. defConfig_HistoryMaxJobs int = 3
  61. )
  62. func defaultConfiguration() configuration {
  63. return configuration{
  64. // Logins
  65. Credentials: configurationCredentials{
  66. Token: placeholderToken,
  67. Email: placeholderEmail,
  68. Password: placeholderPassword,
  69. },
  70. // Owner Settings
  71. Admins: []string{},
  72. AdminChannels: []configurationAdminChannel{},
  73. // Program Settings
  74. ProcessLimit: defConfig_ProcessLimit,
  75. Debug: defConfig_Debug,
  76. BackupDatabaseOnStart: false,
  77. WatchSettings: true,
  78. SettingsOutput: true,
  79. MessageOutput: true,
  80. MessageOutputHistory: false,
  81. DiscordLogLevel: discordgo.LogError,
  82. DiscordTimeout: defConfig_DiscordTimeout,
  83. DownloadTimeout: defConfig_DownloadTimeout,
  84. DownloadRetryMax: defConfig_DownloadRetryMax,
  85. ExitOnBadConnection: false,
  86. GithubUpdateChecking: defConfig_GithubUpdateChecking,
  87. CommandPrefix: defConfig_CommandPrefix,
  88. ScanOwnMessages: defConfig_ScanOwnMessages,
  89. AllowGeneralCommands: true,
  90. InflateDownloadCount: &defConfig_InflateDownloadCount,
  91. EuropeanNumbers: false,
  92. CheckupRate: defConfig_CheckupRate,
  93. ConnectionCheckRate: defConfig_ConnectionCheckRate,
  94. PresenceRefreshRate: defConfig_PresenceRefreshRate,
  95. // Source Setup Defaults
  96. Save: true,
  97. AllowCommands: true,
  98. ScanEdits: true,
  99. IgnoreBots: true,
  100. SendErrorMessages: false,
  101. SendFileToChannel: "",
  102. SendFileToChannels: []string{},
  103. SendFileDirectly: true,
  104. SendFileCaption: "",
  105. FilenameDateFormat: defConfig_FilenameDateFormat,
  106. FilenameFormat: defConfig_FilenameFormat,
  107. // Appearance
  108. PresenceEnabled: defConfig_PresenceEnabled,
  109. PresenceStatus: defConfig_PresenceStatus,
  110. PresenceType: defConfig_PresenceType,
  111. ReactWhenDownloaded: defConfig_ReactWhenDownloaded,
  112. ReactWhenDownloadedHistory: false,
  113. HistoryTyping: true,
  114. // History
  115. HistoryMaxJobs: defConfig_HistoryMaxJobs,
  116. AutoHistory: false,
  117. AutoHistoryBefore: "",
  118. AutoHistorySince: "",
  119. SendHistoryStatus: true,
  120. SendAutoHistoryStatus: false,
  121. // Rules for Saving
  122. DivideByYear: false,
  123. DivideByMonth: false,
  124. DivideByServer: false,
  125. DivideByChannel: false,
  126. DivideByUser: false,
  127. DivideByType: true,
  128. DivideFoldersUseID: false,
  129. SaveImages: true,
  130. SaveVideos: true,
  131. SaveAudioFiles: true,
  132. SaveTextFiles: false,
  133. SaveOtherFiles: false,
  134. SavePossibleDuplicates: true,
  135. Filters: &configurationSourceFilters{
  136. BlockedExtensions: &[]string{
  137. ".htm",
  138. ".html",
  139. ".php",
  140. ".exe",
  141. ".dll",
  142. ".bin",
  143. ".cmd",
  144. ".sh",
  145. ".py",
  146. ".jar",
  147. },
  148. BlockedPhrases: &[]string{},
  149. },
  150. Duplo: false,
  151. DuploThreshold: 0,
  152. }
  153. }
  154. type configuration struct {
  155. Constants map[string]string `json:"_constants,omitempty"`
  156. // Logins
  157. Credentials configurationCredentials `json:"credentials"` // required
  158. // Owner Settings
  159. Admins []string `json:"admins"` // optional
  160. AdminChannels []configurationAdminChannel `json:"adminChannels"` // optional
  161. // Program Settings
  162. ProcessLimit int `json:"processLimit,omitempty"` // optional, defaults
  163. Debug bool `json:"debug,omitempty"` // optional, defaults
  164. BackupDatabaseOnStart bool `json:"backupDatabaseOnStart,omitempty"` // optional, defaults
  165. WatchSettings bool `json:"watchSettings,omitempty"` // optional, defaults
  166. SettingsOutput bool `json:"settingsOutput,omitempty"` // optional, defaults
  167. MessageOutput bool `json:"messageOutput,omitempty"` // optional, defaults
  168. MessageOutputHistory bool `json:"messageOutputHistory,omitempty"` // optional, defaults
  169. DiscordLogLevel int `json:"discordLogLevel,omitempty"` // optional, defaults
  170. DiscordTimeout int `json:"discordTimeout,omitempty"` // optional, defaults
  171. DownloadTimeout int `json:"downloadTimeout,omitempty"` // optional, defaults
  172. DownloadRetryMax int `json:"downloadRetryMax,omitempty"` // optional, defaults
  173. ExitOnBadConnection bool `json:"exitOnBadConnection,omitempty"` // optional, defaults
  174. GithubUpdateChecking bool `json:"githubUpdateChecking,omitempty"` // optional, defaults
  175. CommandPrefix string `json:"commandPrefix,omitempty"` // optional, defaults
  176. ScanOwnMessages bool `json:"scanOwnMessages,omitempty"` // optional, defaults
  177. AllowGeneralCommands bool `json:"allowGeneralCommands,omitempty"` // optional, defaults
  178. InflateDownloadCount *int64 `json:"inflateDownloadCount,omitempty"` // optional, defaults to 0 if undefined
  179. EuropeanNumbers bool `json:"europeanNumbers,omitempty"` // optional, defaults
  180. CheckupRate int `json:"checkupRate,omitempty"` // optional, defaults
  181. ConnectionCheckRate int `json:"connectionCheckRate,omitempty"` // optional, defaults
  182. PresenceRefreshRate int `json:"presenceRefreshRate,omitempty"` // optional, defaults
  183. // Source Setup Defaults
  184. Save bool `json:"save,omitempty"` // optional, defaults
  185. AllowCommands bool `json:"allowCommands,omitempty"` // optional, defaults
  186. ScanEdits bool `json:"scanEdits,omitempty"` // optional, defaults
  187. IgnoreBots bool `json:"ignoreBots,omitempty"` // optional, defaults
  188. SendErrorMessages bool `json:"sendErrorMessages,omitempty"` // optional, defaults
  189. SendFileToChannel string `json:"sendFileToChannel,omitempty"` // optional, defaults
  190. SendFileToChannels []string `json:"sendFileToChannels,omitempty"` // optional, defaults
  191. SendFileDirectly bool `json:"sendFileDirectly,omitempty"` // optional, defaults
  192. SendFileCaption string `json:"sendFileCaption,omitempty"` // optional
  193. FilenameDateFormat string `json:"filenameDateFormat,omitempty"` // optional, defaults
  194. FilenameFormat string `json:"filenameFormat,omitempty"` // optional, defaults
  195. // Appearance
  196. PresenceEnabled bool `json:"presenceEnabled,omitempty"` // optional, defaults
  197. PresenceStatus string `json:"presenceStatus,omitempty"` // optional, defaults
  198. PresenceType discordgo.GameType `json:"presenceType,omitempty"` // optional, defaults
  199. PresenceLabel *string `json:"presenceLabel,omitempty"` // optional, unused if undefined
  200. PresenceDetails *string `json:"presenceDetails,omitempty"` // optional, unused if undefined
  201. PresenceState *string `json:"presenceState,omitempty"` // optional, unused if undefined
  202. ReactWhenDownloaded bool `json:"reactWhenDownloaded,omitempty"` // optional, defaults
  203. ReactWhenDownloadedEmoji *string `json:"reactWhenDownloadedEmoji,omitempty"` // optional
  204. ReactWhenDownloadedHistory bool `json:"reactWhenDownloadedHistory,omitempty"` // optional, defaults
  205. HistoryTyping bool `json:"historyTyping,omitempty"` // optional, defaults
  206. EmbedColor *string `json:"embedColor,omitempty"` // optional, defaults to role if undefined, then defaults random if no role color
  207. // History
  208. HistoryMaxJobs int `json:"historyMaxJobs,omitempty"` // optional, defaults
  209. AutoHistory bool `json:"autoHistory,omitempty"` // optional, defaults
  210. AutoHistoryBefore string `json:"autoHistoryBefore,omitempty"` // optional
  211. AutoHistorySince string `json:"autoHistorySince,omitempty"` // optional
  212. SendAutoHistoryStatus bool `json:"sendAutoHistoryStatus,omitempty"` // optional, defaults
  213. SendHistoryStatus bool `json:"sendHistoryStatus,omitempty"` // optional, defaults
  214. // Rules for Saving
  215. DivideByYear bool `json:"divideByYear,omitempty"` // defaults
  216. DivideByMonth bool `json:"divideByMonth,omitempty"` // defaults
  217. DivideByServer bool `json:"divideByServer,omitempty"` // defaults
  218. DivideByChannel bool `json:"divideByChannel,omitempty"` // defaults
  219. DivideByUser bool `json:"divideByUser,omitempty"` // defaults
  220. DivideByType bool `json:"divideByType,omitempty"` // defaults
  221. DivideFoldersUseID bool `json:"divideFoldersUseID,omitempty"` // defaults
  222. SaveImages bool `json:"saveImages,omitempty"` // defaults
  223. SaveVideos bool `json:"saveVideos,omitempty"` // defaults
  224. SaveAudioFiles bool `json:"saveAudioFiles,omitempty"` // defaults
  225. SaveTextFiles bool `json:"saveTextFiles,omitempty"` // defaults
  226. SaveOtherFiles bool `json:"saveOtherFiles,omitempty"` // defaults
  227. SavePossibleDuplicates bool `json:"savePossibleDuplicates,omitempty"` // defaults
  228. Filters *configurationSourceFilters `json:"filters,omitempty"` // optional
  229. Duplo bool `json:"duplo,omitempty"` // optional, defaults
  230. DuploThreshold float64 `json:"duploThreshold,omitempty"` // optional, defaults
  231. // Sources
  232. All *configurationSource `json:"all,omitempty"`
  233. AllBlacklistUsers *[]string `json:"allBlacklistUsers,omitempty"`
  234. AllBlacklistServers *[]string `json:"allBlacklistServers,omitempty"`
  235. AllBlacklistCategories *[]string `json:"allBlacklistCategories,omitempty"`
  236. AllBlacklistChannels *[]string `json:"allBlacklistChannels,omitempty"`
  237. Users []configurationSource `json:"users,omitempty"`
  238. Servers []configurationSource `json:"servers,omitempty"`
  239. Categories []configurationSource `json:"categories,omitempty"`
  240. Channels []configurationSource `json:"channels,omitempty"`
  241. }
  242. type constStruct struct {
  243. Constants map[string]string `json:"_constants,omitempty"`
  244. }
  245. //#endregion
  246. //#region Config, Sources
  247. // Needed for settings used without redundant nil checks, and settings defaulting + creation
  248. var (
  249. defSource_Enabled bool = true
  250. )
  251. type configurationSource struct {
  252. // ~
  253. UserID string `json:"user,omitempty"` // used for config.Users
  254. UserIDs *[]string `json:"users,omitempty"` // ---> alternative to UserID
  255. ServerID string `json:"server,omitempty"` // used for config.Servers
  256. ServerIDs *[]string `json:"servers,omitempty"` // ---> alternative to ServerID
  257. ServerBlacklist *[]string `json:"serverBlacklist,omitempty"` // for server.ServerID & server.ServerIDs
  258. CategoryID string `json:"category,omitempty"` // used for config.Categories
  259. CategoryIDs *[]string `json:"categories,omitempty"` // ---> alternative to CategoryID
  260. CategoryBlacklist *[]string `json:"categoryBlacklist,omitempty"` // for server.CategoryID & server.CategoryIDs
  261. ChannelID string `json:"channel,omitempty"` // used for config.Channels
  262. ChannelIDs *[]string `json:"channels,omitempty"` // ---> alternative to ChannelID
  263. Destination string `json:"destination"` // required
  264. // Setup
  265. Enabled *bool `json:"enabled"` // optional, defaults
  266. Save *bool `json:"save"` // optional, defaults
  267. AllowCommands *bool `json:"allowCommands,omitempty"` // optional, defaults
  268. ScanEdits *bool `json:"scanEdits,omitempty"` // optional, defaults
  269. IgnoreBots *bool `json:"ignoreBots,omitempty"` // optional, defaults
  270. SendErrorMessages *bool `json:"sendErrorMessages,omitempty"` // optional, defaults
  271. SendFileToChannel *string `json:"sendFileToChannel,omitempty"` // optional, defaults
  272. SendFileToChannels *[]string `json:"sendFileToChannels,omitempty"` // optional, defaults
  273. SendFileDirectly *bool `json:"sendFileDirectly,omitempty"` // optional, defaults
  274. SendFileCaption *string `json:"sendFileCaption,omitempty"` // optional
  275. FilenameDateFormat *string `json:"filenameDateFormat,omitempty"` // optional
  276. FilenameFormat *string `json:"filenameFormat,omitempty"` // optional
  277. // Appearance
  278. PresenceEnabled *bool `json:"presenceEnabled,omitempty"` // optional, defaults
  279. ReactWhenDownloaded *bool `json:"reactWhenDownloaded,omitempty"` // optional, defaults
  280. ReactWhenDownloadedEmoji *string `json:"reactWhenDownloadedEmoji,omitempty"` // optional, defaults
  281. ReactWhenDownloadedHistory *bool `json:"reactWhenDownloadedHistory,omitempty"` // optional, defaults
  282. BlacklistReactEmojis *[]string `json:"blacklistReactEmojis,omitempty"` // optional
  283. HistoryTyping *bool `json:"historyTyping,omitempty"` // optional, defaults
  284. EmbedColor *string `json:"embedColor,omitempty"` // optional, defaults to role if undefined, then defaults random if no role color
  285. // History
  286. AutoHistory *bool `json:"autoHistory,omitempty"` // optional
  287. AutoHistoryBefore *string `json:"autoHistoryBefore,omitempty"` // optional
  288. AutoHistorySince *string `json:"autoHistorySince,omitempty"` // optional
  289. SendAutoHistoryStatus *bool `json:"sendAutoHistoryStatus,omitempty"` // optional, defaults
  290. SendHistoryStatus *bool `json:"sendHistoryStatus,omitempty"` // optional, defaults
  291. // Rules for Saving
  292. DivideByYear *bool `json:"divideByYear,omitempty"` // optional, defaults
  293. DivideByMonth *bool `json:"divideByMonth,omitempty"` // optional, defaults
  294. DivideByServer *bool `json:"divideByServer,omitempty"` // optional, defaults
  295. DivideByChannel *bool `json:"divideByChannel,omitempty"` // optional, defaults
  296. DivideByUser *bool `json:"divideByUser,omitempty"` // optional, defaults
  297. DivideByType *bool `json:"divideByType,omitempty"` // optional, defaults
  298. DivideFoldersUseID *bool `json:"divideFoldersUseID,omitempty"` // optional, defaults
  299. SaveImages *bool `json:"saveImages,omitempty"` // optional, defaults
  300. SaveVideos *bool `json:"saveVideos,omitempty"` // optional, defaults
  301. SaveAudioFiles *bool `json:"saveAudioFiles,omitempty"` // optional, defaults
  302. SaveTextFiles *bool `json:"saveTextFiles,omitempty"` // optional, defaults
  303. SaveOtherFiles *bool `json:"saveOtherFiles,omitempty"` // optional, defaults
  304. SavePossibleDuplicates *bool `json:"savePossibleDuplicates,omitempty"` // optional, defaults
  305. Filters *configurationSourceFilters `json:"filters,omitempty"` // optional
  306. Duplo *bool `json:"duplo,omitempty"` // optional, defaults
  307. DuploThreshold *float64 `json:"duploThreshold,omitempty"` // optional, defaults
  308. // Misc Rules
  309. LogLinks *configurationSourceLog `json:"logLinks,omitempty"` // optional
  310. LogMessages *configurationSourceLog `json:"logMessages,omitempty"` // optional
  311. }
  312. type configurationSourceFilters struct {
  313. BlockedPhrases *[]string `json:"blockedPhrases,omitempty"` // optional
  314. AllowedPhrases *[]string `json:"allowedPhrases,omitempty"` // optional
  315. BlockedUsers *[]string `json:"blockedUsers,omitempty"` // optional
  316. AllowedUsers *[]string `json:"allowedUsers,omitempty"` // optional
  317. BlockedRoles *[]string `json:"blockedRoles,omitempty"` // optional
  318. AllowedRoles *[]string `json:"allowedRoles,omitempty"` // optional
  319. BlockedDomains *[]string `json:"blockedDomains,omitempty"` // optional
  320. AllowedDomains *[]string `json:"allowedDomains,omitempty"` // optional
  321. BlockedExtensions *[]string `json:"blockedExtensions,omitempty"` // optional
  322. AllowedExtensions *[]string `json:"allowedExtensions,omitempty"` // optional
  323. BlockedFilenames *[]string `json:"blockedFilenames,omitempty"` // optional
  324. AllowedFilenames *[]string `json:"allowedFilenames,omitempty"` // optional
  325. BlockedReactions *[]string `json:"blockedReactions,omitempty"` // optional
  326. AllowedReactions *[]string `json:"allowedReactions,omitempty"` // optional
  327. }
  328. var (
  329. defSourceLog_DestinationIsFolder bool = false
  330. defSourceLog_DivideLogsByServer bool = true
  331. defSourceLog_DivideLogsByChannel bool = true
  332. defSourceLog_DivideLogsByUser bool = false
  333. defSourceLog_DivideLogsByStatus bool = false
  334. defSourceLog_LogDownloads bool = true
  335. defSourceLog_LogFailures bool = true
  336. )
  337. type configurationSourceLog struct {
  338. Destination string `json:"destination"` // required
  339. DestinationIsFolder *bool `json:"destinationIsFolder,omitempty"` // optional, defaults
  340. DivideLogsByServer *bool `json:"divideLogsByServer,omitempty"` // optional, defaults
  341. DivideLogsByChannel *bool `json:"divideLogsByChannel,omitempty"` // optional, defaults
  342. DivideLogsByUser *bool `json:"divideLogsByUser,omitempty"` // optional, defaults
  343. DivideLogsByStatus *bool `json:"divideLogsByStatus,omitempty"` // optional, defaults
  344. LogDownloads *bool `json:"logDownloads,omitempty"` // optional, defaults
  345. LogFailures *bool `json:"logFailures,omitempty"` // optional, defaults
  346. FilterDuplicates *bool `json:"filterDuplicates,omitempty"` // optional, defaults
  347. Prefix *string `json:"prefix,omitempty"` // optional
  348. Suffix *string `json:"suffix,omitempty"` // optional
  349. UserData *bool `json:"userData,omitempty"` // optional, defaults
  350. }
  351. //#endregion
  352. //#region Config, Admin Channels
  353. var (
  354. adefConfig_LogProgram bool = false
  355. adefConfig_LogStatus bool = true
  356. adefConfig_LogErrors bool = true
  357. adefConfig_UnlockCommands bool = false
  358. )
  359. type configurationAdminChannel struct {
  360. ChannelID string `json:"channel"` // required
  361. ChannelIDs *[]string `json:"channels,omitempty"` // ---> alternative to ChannelID
  362. LogProgram *bool `json:"logProgram,omitempty"` // optional, defaults
  363. LogStatus *bool `json:"logStatus,omitempty"` // optional, defaults
  364. LogErrors *bool `json:"logErrors,omitempty"` // optional, defaults
  365. UnlockCommands *bool `json:"unlockCommands,omitempty"` // optional, defaults
  366. /* IDEAS / TODO:
  367. LogMessages *bool `json:"logMessages,omitempty"` // optional, defaults
  368. LogLinks *bool `json:"logLinks,omitempty"` // optional, defaults
  369. LogFiles *bool `json:"logFiles,omitempty"` // optional, defaults
  370. */
  371. }
  372. //#endregion
  373. //#region Management
  374. func loadConfig() error {
  375. // Determine json type
  376. if _, err := os.Stat(configFileBase + ".jsonc"); err == nil {
  377. configFile = configFileBase + ".jsonc"
  378. configFileC = true
  379. } else {
  380. configFile = configFileBase + ".json"
  381. configFileC = false
  382. }
  383. log.Println(lg("Settings", "loadConfig", color.YellowString, "Loading from \"%s\"...", configFile))
  384. // Load settings
  385. configContent, err := os.ReadFile(configFile)
  386. if err != nil {
  387. log.Println(lg("Settings", "loadConfig", color.HiRedString, "Failed to open file...\t%s", err))
  388. createConfig()
  389. properExit()
  390. } else {
  391. fixed := string(configContent)
  392. // Fix backslashes
  393. fixed = strings.ReplaceAll(fixed, "\\", "\\\\")
  394. for strings.Contains(fixed, "\\\\\\") {
  395. fixed = strings.ReplaceAll(fixed, "\\\\\\", "\\\\")
  396. }
  397. //TODO: Not even sure if this is realistic to do but would be nice to have line comma & trailing comma fixing
  398. // Parse
  399. newConfig := defaultConfiguration()
  400. if configFileC {
  401. err = jsonc.Unmarshal([]byte(fixed), &newConfig)
  402. } else {
  403. err = json.Unmarshal([]byte(fixed), &newConfig)
  404. }
  405. if err != nil {
  406. log.Println(lg("Settings", "loadConfig", color.HiRedString, "Failed to parse settings file...\t%s", err))
  407. log.Println(lg("Settings", "loadConfig", color.MagentaString, "Please ensure you're following proper JSON format syntax."))
  408. properExit()
  409. }
  410. // Constants
  411. if newConfig.Constants != nil {
  412. for key, value := range newConfig.Constants {
  413. if strings.Contains(fixed, key) {
  414. fixed = strings.ReplaceAll(fixed, key, value)
  415. }
  416. }
  417. // Re-parse
  418. newConfig = defaultConfiguration()
  419. if configFileC {
  420. err = jsonc.Unmarshal([]byte(fixed), &newConfig)
  421. } else {
  422. err = json.Unmarshal([]byte(fixed), &newConfig)
  423. }
  424. if err != nil {
  425. log.Println(lg("Settings", "loadConfig", color.HiRedString,
  426. "Failed to re-parse settings file after replacing constants...\t%s", err))
  427. log.Println(lg("Settings", "loadConfig", color.MagentaString, "Please ensure you're following proper JSON format syntax."))
  428. properExit()
  429. }
  430. newConfig.Constants = nil
  431. }
  432. config = newConfig
  433. // Source Defaults
  434. for i := 0; i < len(config.Channels); i++ {
  435. sourceDefault(&config.Channels[i])
  436. }
  437. for i := 0; i < len(config.Categories); i++ {
  438. sourceDefault(&config.Categories[i])
  439. }
  440. for i := 0; i < len(config.Servers); i++ {
  441. sourceDefault(&config.Servers[i])
  442. }
  443. for i := 0; i < len(config.Users); i++ {
  444. sourceDefault(&config.Users[i])
  445. }
  446. if config.All != nil {
  447. sourceDefault(config.All)
  448. }
  449. // Admin Channel Defaults
  450. for i := 0; i < len(config.AdminChannels); i++ {
  451. adminChannelDefault(&config.AdminChannels[i])
  452. }
  453. // Checks & Fixes
  454. if config.ProcessLimit < 1 {
  455. config.ProcessLimit = defConfig_ProcessLimit
  456. }
  457. if config.DiscordTimeout < 10 {
  458. config.DiscordTimeout = defConfig_DiscordTimeout
  459. }
  460. if config.DownloadTimeout < 10 {
  461. config.DownloadTimeout = defConfig_DownloadTimeout
  462. }
  463. if config.DownloadRetryMax < 1 {
  464. config.DownloadRetryMax = defConfig_DownloadRetryMax
  465. }
  466. if config.CheckupRate < 1 {
  467. config.CheckupRate = defConfig_CheckupRate
  468. }
  469. if config.ConnectionCheckRate < 1 {
  470. config.ConnectionCheckRate = defConfig_ConnectionCheckRate
  471. }
  472. if config.PresenceRefreshRate < 1 {
  473. config.PresenceRefreshRate = defConfig_PresenceRefreshRate
  474. }
  475. if config.FilenameDateFormat == "" {
  476. config.FilenameDateFormat = defConfig_FilenameDateFormat
  477. }
  478. if config.FilenameFormat == "" {
  479. config.FilenameFormat = defConfig_FilenameFormat
  480. }
  481. if config.HistoryMaxJobs < 1 {
  482. config.HistoryMaxJobs = defConfig_HistoryMaxJobs
  483. }
  484. // Settings Output
  485. if config.SettingsOutput {
  486. dupeConfig := config
  487. if dupeConfig.Credentials.Token != "" && dupeConfig.Credentials.Token != placeholderToken {
  488. dupeConfig.Credentials.Token = "STRIPPED_FOR_OUTPUT"
  489. }
  490. if dupeConfig.Credentials.Email != "" && dupeConfig.Credentials.Email != placeholderEmail {
  491. dupeConfig.Credentials.Email = "STRIPPED_FOR_OUTPUT"
  492. }
  493. if dupeConfig.Credentials.Password != "" && dupeConfig.Credentials.Password != placeholderPassword {
  494. dupeConfig.Credentials.Password = "STRIPPED_FOR_OUTPUT"
  495. }
  496. if dupeConfig.Credentials.TwitterUsername != "" {
  497. dupeConfig.Credentials.TwitterUsername = "STRIPPED_FOR_OUTPUT"
  498. }
  499. if dupeConfig.Credentials.TwitterPassword != "" {
  500. dupeConfig.Credentials.TwitterPassword = "STRIPPED_FOR_OUTPUT"
  501. }
  502. if dupeConfig.Credentials.InstagramUsername != "" {
  503. dupeConfig.Credentials.InstagramUsername = "STRIPPED_FOR_OUTPUT"
  504. }
  505. if dupeConfig.Credentials.InstagramPassword != "" {
  506. dupeConfig.Credentials.InstagramPassword = "STRIPPED_FOR_OUTPUT"
  507. }
  508. if dupeConfig.Credentials.FlickrApiKey != "" {
  509. dupeConfig.Credentials.FlickrApiKey = "STRIPPED_FOR_OUTPUT"
  510. }
  511. s, err := json.MarshalIndent(dupeConfig, "", "\t")
  512. if err != nil {
  513. log.Println(lg("Debug", "loadConfig", color.HiRedString, "Failed to output...\t%s", err))
  514. } else {
  515. log.Println(lg("Debug", "loadConfig", color.HiYellowString, "Parsed into JSON:\n\n%s", color.YellowString(string(s))))
  516. }
  517. }
  518. // Credentials Check
  519. if (config.Credentials.Token == "" || config.Credentials.Token == placeholderToken) &&
  520. (config.Credentials.Email == "" || config.Credentials.Email == placeholderEmail) &&
  521. (config.Credentials.Password == "" || config.Credentials.Password == placeholderPassword) {
  522. log.Println(lg("Settings", "loadConfig", color.HiRedString, "No valid discord login found..."))
  523. log.Println(lg("Settings", "loadConfig", color.HiYellowString, "Please save your credentials & info into \"%s\" then restart...", configFile))
  524. log.Println(lg("Settings", "loadConfig", color.MagentaString, "If your credentials are already properly saved, please ensure you're following proper JSON format syntax..."))
  525. log.Println(lg("Settings", "loadConfig", color.MagentaString, "You DO NOT NEED token *AND* email/password, just one OR the other."))
  526. properExit()
  527. }
  528. allString := ""
  529. if config.All != nil {
  530. allString = ", ALL ENABLED"
  531. }
  532. log.Println(lg("Settings", "", color.HiYellowString,
  533. "Loaded - bound to %d channel%s, %d categories, %d server%s, %d user%s%s",
  534. getBoundChannelsCount(), pluralS(getBoundChannelsCount()),
  535. getBoundCategoriesCount(),
  536. getBoundServersCount(), pluralS(getBoundServersCount()),
  537. getBoundUsersCount(), pluralS(getBoundUsersCount()), allString,
  538. ))
  539. // SETTINGS TO BE APPLIED IMMEDIATELY
  540. if config.ProcessLimit > 0 {
  541. runtime.GOMAXPROCS(config.ProcessLimit)
  542. }
  543. }
  544. return nil
  545. }
  546. func createConfig() {
  547. log.Println(lg("Settings", "create", color.YellowString, "Creating new settings file..."))
  548. enteredBaseChannel := "REPLACE_WITH_DISCORD_CHANNEL_ID_TO_DOWNLOAD_FROM"
  549. enteredBaseDestination := "REPLACE_WITH_FOLDER_LOCATION_TO_DOWNLOAD_TO"
  550. // Separate from Defaultconfiguration because there's some elements we want to strip for settings creation
  551. defaultConfig := configuration{
  552. Credentials: configurationCredentials{
  553. Token: placeholderToken,
  554. Email: placeholderEmail,
  555. Password: placeholderPassword,
  556. },
  557. Admins: []string{"REPLACE_WITH_YOUR_DISCORD_USER_ID"},
  558. CommandPrefix: defConfig_CommandPrefix,
  559. ScanOwnMessages: defConfig_ScanOwnMessages,
  560. PresenceEnabled: defConfig_PresenceEnabled,
  561. PresenceStatus: defConfig_PresenceStatus,
  562. PresenceType: defConfig_PresenceType,
  563. ReactWhenDownloaded: defConfig_ReactWhenDownloaded,
  564. GithubUpdateChecking: defConfig_GithubUpdateChecking,
  565. Debug: defConfig_Debug,
  566. }
  567. // Import old config
  568. if _, err := os.Stat("config.ini"); err == nil {
  569. log.Println(lg("Settings", "create", color.HiGreenString,
  570. "Detected config.ini from Seklfreak's discord-image-downloader-go, importing..."))
  571. cfg, err := ini.Load("config.ini")
  572. if err != nil {
  573. log.Println(lg("Settings", "create", color.HiRedString,
  574. "Unable to read your old config file:\t%s", err))
  575. cfg = ini.Empty()
  576. } else { // Import old ini
  577. importKey := func(section string, key string, outVar interface{}, outType string) bool {
  578. if cfg.Section(section).HasKey(key) {
  579. if outType == "string" {
  580. outVar = cfg.Section(section).Key(key).String()
  581. } else if outType == "int" {
  582. outVar = cfg.Section(section).Key(key).MustInt()
  583. } else if outType == "bool" {
  584. outVar = cfg.Section(section).Key(key).MustBool()
  585. }
  586. log.Println(lg("Settings", "create", color.GreenString, "IMPORTED %s - %s:\t\t\t%s", section, key, outVar))
  587. return true
  588. }
  589. return false
  590. }
  591. // Auth
  592. if !importKey("auth", "token", &defaultConfig.Credentials.Token, "string") {
  593. defaultConfig.Credentials.Token = ""
  594. }
  595. if !importKey("auth", "email", &defaultConfig.Credentials.Email, "string") {
  596. defaultConfig.Credentials.Email = ""
  597. }
  598. if !importKey("auth", "password", &defaultConfig.Credentials.Password, "string") {
  599. defaultConfig.Credentials.Password = ""
  600. }
  601. importKey("flickr", "api key", &defaultConfig.Credentials.FlickrApiKey, "string")
  602. // General
  603. importKey("general", "max download retries", &defaultConfig.DownloadRetryMax, "int")
  604. importKey("general", "download timeout", &defaultConfig.DownloadTimeout, "int")
  605. // Status
  606. importKey("status", "status enabled", &defaultConfig.PresenceEnabled, "bool")
  607. importKey("status", "status type", &defaultConfig.PresenceStatus, "string")
  608. importKey("status", "status label", &defaultConfig.PresenceType, "int")
  609. // Channels
  610. InteractiveChannelWhitelist := cfg.Section("interactive channels").KeysHash()
  611. for key := range InteractiveChannelWhitelist {
  612. newChannel := configurationAdminChannel{
  613. ChannelID: key,
  614. }
  615. log.Println(lg("Settings", "create", color.GreenString, "IMPORTED Admin Channel:\t\t%s", key))
  616. defaultConfig.AdminChannels = append(defaultConfig.AdminChannels, newChannel)
  617. }
  618. ChannelWhitelist := cfg.Section("channels").KeysHash()
  619. for key, value := range ChannelWhitelist {
  620. newChannel := configurationSource{
  621. ChannelID: key,
  622. Destination: value,
  623. }
  624. log.Println(lg("Settings", "create", color.GreenString, "IMPORTED Channel:\t\t\t%s to \"%s\"", key, value))
  625. defaultConfig.Channels = append(defaultConfig.Channels, newChannel)
  626. }
  627. }
  628. log.Println(lg("Settings", "create", color.HiGreenString,
  629. "Finished importing config.ini from Seklfreak's discord-image-downloader-go!"))
  630. } else {
  631. baseChannel := configurationSource{
  632. ChannelID: enteredBaseChannel,
  633. Destination: enteredBaseDestination,
  634. Enabled: &defSource_Enabled,
  635. Save: &config.Save,
  636. AllowCommands: &config.AllowCommands,
  637. SendErrorMessages: &config.SendErrorMessages,
  638. ScanEdits: &config.ScanEdits,
  639. IgnoreBots: &config.IgnoreBots,
  640. PresenceEnabled: &config.PresenceEnabled,
  641. ReactWhenDownloadedHistory: &config.ReactWhenDownloadedHistory,
  642. DivideByType: &config.DivideByType,
  643. SaveImages: &config.SaveImages,
  644. SaveVideos: &config.SaveVideos,
  645. }
  646. defaultConfig.Channels = append(defaultConfig.Channels, baseChannel)
  647. baseAdminChannel := configurationAdminChannel{
  648. ChannelID: "REPLACE_WITH_DISCORD_CHANNEL_ID_FOR_ADMIN_COMMANDS",
  649. }
  650. defaultConfig.AdminChannels = append(defaultConfig.AdminChannels, baseAdminChannel)
  651. //TODO: Improve, this is very crude, I just wanted *something* for this.
  652. log.Print(lg("Settings", "create", color.HiCyanString, "Would you like to enter settings info now? [Y/N]: "))
  653. reader := bufio.NewReader(os.Stdin)
  654. inputCredsYN, _ := reader.ReadString('\n')
  655. inputCredsYN = strings.ReplaceAll(inputCredsYN, "\n", "")
  656. inputCredsYN = strings.ReplaceAll(inputCredsYN, "\r", "")
  657. if strings.Contains(strings.ToLower(inputCredsYN), "y") {
  658. EnterCreds:
  659. log.Print(color.HiCyanString("Token or Login? [\"token\"/\"login\"]: "))
  660. inputCreds, _ := reader.ReadString('\n')
  661. inputCreds = strings.ReplaceAll(inputCreds, "\n", "")
  662. inputCreds = strings.ReplaceAll(inputCreds, "\r", "")
  663. if strings.Contains(strings.ToLower(inputCreds), "token") {
  664. EnterToken:
  665. log.Print(color.HiCyanString("Enter token: "))
  666. inputToken, _ := reader.ReadString('\n')
  667. inputToken = strings.ReplaceAll(inputToken, "\n", "")
  668. inputToken = strings.ReplaceAll(inputToken, "\r", "")
  669. if inputToken != "" {
  670. defaultConfig.Credentials.Token = inputToken
  671. } else {
  672. log.Println(lg("Settings", "create", color.HiRedString, "Please input token..."))
  673. goto EnterToken
  674. }
  675. } else if strings.Contains(strings.ToLower(inputCreds), "login") {
  676. EnterEmail:
  677. log.Print(color.HiCyanString("Enter email: "))
  678. inputEmail, _ := reader.ReadString('\n')
  679. inputEmail = strings.ReplaceAll(inputEmail, "\n", "")
  680. inputEmail = strings.ReplaceAll(inputEmail, "\r", "")
  681. if strings.Contains(inputEmail, "@") {
  682. defaultConfig.Credentials.Email = inputEmail
  683. EnterPassword:
  684. log.Print(color.HiCyanString("Enter password: "))
  685. inputPassword, _ := reader.ReadString('\n')
  686. inputPassword = strings.ReplaceAll(inputPassword, "\n", "")
  687. inputPassword = strings.ReplaceAll(inputPassword, "\r", "")
  688. if inputPassword != "" {
  689. defaultConfig.Credentials.Password = inputPassword
  690. } else {
  691. log.Println(lg("Settings", "create", color.HiRedString, "Please input password..."))
  692. goto EnterPassword
  693. }
  694. } else {
  695. log.Println(lg("Settings", "create", color.HiRedString, "Please input email..."))
  696. goto EnterEmail
  697. }
  698. } else {
  699. log.Println(lg("Settings", "create", color.HiRedString, "Please input \"token\" or \"login\"..."))
  700. goto EnterCreds
  701. }
  702. EnterAdmin:
  703. log.Print(color.HiCyanString("Input your Discord User ID: "))
  704. inputAdmin, _ := reader.ReadString('\n')
  705. inputAdmin = strings.ReplaceAll(inputAdmin, "\n", "")
  706. inputAdmin = strings.ReplaceAll(inputAdmin, "\r", "")
  707. if isNumeric(inputAdmin) {
  708. defaultConfig.Admins = []string{inputAdmin}
  709. } else {
  710. log.Println(lg("Settings", "create", color.HiRedString, "Please input your Discord User ID..."))
  711. goto EnterAdmin
  712. }
  713. //TODO: Base channel setup? Would be kind of annoying and may limit options
  714. //TODO: Admin channel setup?
  715. }
  716. }
  717. log.Println(lg("Settings", "create", color.MagentaString,
  718. "The default settings will be missing some options to avoid clutter."))
  719. log.Println(lg("Settings", "create", color.HiMagentaString,
  720. "There are MANY MORE SETTINGS! If you would like to maximize customization, see the GitHub README for all available settings."))
  721. defaultJSON, err := json.MarshalIndent(defaultConfig, "", "\t")
  722. if err != nil {
  723. log.Println(lg("Settings", "create", color.HiRedString, "Failed to format new settings...\t%s", err))
  724. } else {
  725. err := os.WriteFile(configFile, defaultJSON, 0644)
  726. if err != nil {
  727. log.Println(lg("Settings", "create", color.HiRedString, "Failed to save new settings file...\t%s", err))
  728. } else {
  729. log.Println(lg("Settings", "create", color.HiYellowString, "Created new settings file..."))
  730. log.Println(lg("Settings", "create", color.HiYellowString,
  731. "Please save your credentials & info into \"%s\" then restart...", configFile))
  732. log.Println(lg("Settings", "create", color.MagentaString,
  733. "You DO NOT NEED token *AND* email/password, just one OR the other."))
  734. log.Println(lg("Settings", "create", color.MagentaString,
  735. "THERE ARE MANY HIDDEN SETTINGS AVAILABLE, SEE THE GITHUB README github.com/"+projectRepoBase))
  736. }
  737. }
  738. }
  739. func sourceDefault(channel *configurationSource) {
  740. // These have to use the default variables since literal values and consts can't be set to the pointers
  741. // Setup
  742. if channel.Enabled == nil {
  743. channel.Enabled = &defSource_Enabled
  744. }
  745. if channel.Save == nil {
  746. channel.Save = &config.Save
  747. }
  748. if channel.AllowCommands == nil {
  749. channel.AllowCommands = &config.AllowCommands
  750. }
  751. if channel.SendErrorMessages == nil {
  752. channel.SendErrorMessages = &config.SendErrorMessages
  753. }
  754. if channel.ScanEdits == nil {
  755. channel.ScanEdits = &config.ScanEdits
  756. }
  757. if channel.IgnoreBots == nil {
  758. channel.IgnoreBots = &config.IgnoreBots
  759. }
  760. if channel.SendErrorMessages == nil {
  761. channel.SendErrorMessages = &config.SendErrorMessages
  762. }
  763. if channel.SendFileToChannel == nil && config.SendFileToChannel != "" {
  764. channel.SendFileToChannel = &config.SendFileToChannel
  765. }
  766. if channel.SendFileToChannels == nil && config.SendFileToChannels != nil {
  767. channel.SendFileToChannels = &config.SendFileToChannels
  768. }
  769. if channel.SendFileDirectly == nil {
  770. channel.SendFileDirectly = &config.SendFileDirectly
  771. }
  772. if channel.SendFileCaption == nil && config.SendFileCaption != "" {
  773. channel.SendFileCaption = &config.SendFileCaption
  774. }
  775. if channel.FilenameDateFormat == nil {
  776. channel.FilenameDateFormat = &config.FilenameDateFormat
  777. }
  778. if channel.FilenameFormat == nil {
  779. channel.FilenameFormat = &config.FilenameFormat
  780. }
  781. // Appearance
  782. if channel.PresenceEnabled == nil {
  783. channel.PresenceEnabled = &config.PresenceEnabled
  784. }
  785. if channel.ReactWhenDownloaded == nil {
  786. channel.ReactWhenDownloaded = &config.ReactWhenDownloaded
  787. }
  788. if channel.ReactWhenDownloadedEmoji == nil && config.ReactWhenDownloadedEmoji != nil {
  789. channel.ReactWhenDownloadedEmoji = config.ReactWhenDownloadedEmoji
  790. }
  791. if channel.ReactWhenDownloadedHistory == nil {
  792. channel.ReactWhenDownloadedHistory = &config.ReactWhenDownloadedHistory
  793. }
  794. if channel.BlacklistReactEmojis == nil {
  795. channel.BlacklistReactEmojis = &[]string{}
  796. }
  797. if channel.HistoryTyping == nil {
  798. channel.HistoryTyping = &config.HistoryTyping
  799. }
  800. if channel.EmbedColor == nil && config.EmbedColor != nil {
  801. channel.EmbedColor = config.EmbedColor
  802. }
  803. // History
  804. if channel.AutoHistory == nil {
  805. channel.AutoHistory = &config.AutoHistory
  806. }
  807. if channel.AutoHistoryBefore == nil {
  808. channel.AutoHistoryBefore = &config.AutoHistoryBefore
  809. }
  810. if channel.AutoHistorySince == nil {
  811. channel.AutoHistorySince = &config.AutoHistorySince
  812. }
  813. if channel.SendAutoHistoryStatus == nil {
  814. channel.SendAutoHistoryStatus = &config.SendAutoHistoryStatus
  815. }
  816. if channel.SendHistoryStatus == nil {
  817. channel.SendHistoryStatus = &config.SendHistoryStatus
  818. }
  819. // Rules for Saving
  820. if channel.DivideByYear == nil {
  821. channel.DivideByYear = &config.DivideByYear
  822. }
  823. if channel.DivideByMonth == nil {
  824. channel.DivideByMonth = &config.DivideByMonth
  825. }
  826. if channel.DivideByServer == nil {
  827. channel.DivideByServer = &config.DivideByServer
  828. }
  829. if channel.DivideByChannel == nil {
  830. channel.DivideByChannel = &config.DivideByChannel
  831. }
  832. if channel.DivideByUser == nil {
  833. channel.DivideByUser = &config.DivideByUser
  834. }
  835. if channel.DivideByType == nil {
  836. channel.DivideByType = &config.DivideByType
  837. }
  838. if channel.DivideFoldersUseID == nil {
  839. channel.DivideFoldersUseID = &config.DivideFoldersUseID
  840. }
  841. if channel.SaveImages == nil {
  842. channel.SaveImages = &config.SaveImages
  843. }
  844. if channel.SaveVideos == nil {
  845. channel.SaveVideos = &config.SaveVideos
  846. }
  847. if channel.SaveAudioFiles == nil {
  848. channel.SaveAudioFiles = &config.SaveAudioFiles
  849. }
  850. if channel.SaveTextFiles == nil {
  851. channel.SaveTextFiles = &config.SaveTextFiles
  852. }
  853. if channel.SaveOtherFiles == nil {
  854. channel.SaveOtherFiles = &config.SaveOtherFiles
  855. }
  856. if channel.SavePossibleDuplicates == nil {
  857. channel.SavePossibleDuplicates = &config.SavePossibleDuplicates
  858. }
  859. if channel.Filters == nil {
  860. channel.Filters = &configurationSourceFilters{}
  861. }
  862. if channel.Filters.BlockedExtensions == nil && config.Filters.BlockedExtensions != nil {
  863. channel.Filters.BlockedExtensions = config.Filters.BlockedExtensions
  864. }
  865. if channel.Filters.BlockedPhrases == nil && config.Filters.BlockedPhrases != nil {
  866. channel.Filters.BlockedPhrases = config.Filters.BlockedPhrases
  867. }
  868. if channel.Duplo == nil {
  869. channel.Duplo = &config.Duplo
  870. }
  871. if channel.DuploThreshold == nil {
  872. channel.DuploThreshold = &config.DuploThreshold
  873. }
  874. // Misc Rules
  875. if channel.LogLinks == nil {
  876. channel.LogLinks = &configurationSourceLog{}
  877. }
  878. if channel.LogLinks.DestinationIsFolder == nil {
  879. channel.LogLinks.DestinationIsFolder = &defSourceLog_DestinationIsFolder
  880. }
  881. if channel.LogLinks.DivideLogsByServer == nil {
  882. channel.LogLinks.DivideLogsByServer = &defSourceLog_DivideLogsByServer
  883. }
  884. if channel.LogLinks.DivideLogsByChannel == nil {
  885. channel.LogLinks.DivideLogsByChannel = &defSourceLog_DivideLogsByChannel
  886. }
  887. if channel.LogLinks.DivideLogsByUser == nil {
  888. channel.LogLinks.DivideLogsByUser = &defSourceLog_DivideLogsByUser
  889. }
  890. if channel.LogLinks.DivideLogsByStatus == nil {
  891. channel.LogLinks.DivideLogsByStatus = &defSourceLog_DivideLogsByStatus
  892. }
  893. if channel.LogLinks.LogDownloads == nil {
  894. channel.LogLinks.LogDownloads = &defSourceLog_LogDownloads
  895. }
  896. if channel.LogLinks.LogFailures == nil {
  897. channel.LogLinks.LogFailures = &defSourceLog_LogFailures
  898. }
  899. if channel.LogMessages == nil {
  900. channel.LogMessages = &configurationSourceLog{}
  901. }
  902. if channel.LogMessages.DestinationIsFolder == nil {
  903. channel.LogMessages.DestinationIsFolder = &defSourceLog_DestinationIsFolder
  904. }
  905. if channel.LogMessages.DivideLogsByServer == nil {
  906. channel.LogMessages.DivideLogsByServer = &defSourceLog_DivideLogsByServer
  907. }
  908. if channel.LogMessages.DivideLogsByChannel == nil {
  909. channel.LogMessages.DivideLogsByChannel = &defSourceLog_DivideLogsByChannel
  910. }
  911. if channel.LogMessages.DivideLogsByUser == nil {
  912. channel.LogMessages.DivideLogsByUser = &defSourceLog_DivideLogsByUser
  913. }
  914. // LAZY CHECKS
  915. if channel.Duplo != nil {
  916. if *channel.Duplo {
  917. sourceHasDuplo = true
  918. }
  919. }
  920. }
  921. var sourceHasDuplo bool = false
  922. func adminChannelDefault(channel *configurationAdminChannel) {
  923. if channel.LogProgram == nil {
  924. channel.LogProgram = &adefConfig_LogProgram
  925. }
  926. if channel.LogStatus == nil {
  927. channel.LogStatus = &adefConfig_LogStatus
  928. }
  929. if channel.LogErrors == nil {
  930. channel.LogErrors = &adefConfig_LogErrors
  931. }
  932. if channel.UnlockCommands == nil {
  933. channel.UnlockCommands = &adefConfig_UnlockCommands
  934. }
  935. }
  936. //#endregion
  937. //#region Functions, Admin & Source
  938. func isNestedMessage(subjectMessage *discordgo.Message, targetChannel string) bool {
  939. if subjectMessage.ID != "" {
  940. _, err := bot.State.Message(targetChannel, subjectMessage.ChannelID)
  941. return err == nil
  942. }
  943. return false
  944. }
  945. var emptyConfig configurationSource = configurationSource{}
  946. func getSource(m *discordgo.Message, c *discordgo.Channel) configurationSource {
  947. subjectID := m.ChannelID
  948. if c != nil {
  949. if c.ParentID != "" {
  950. subjectID = c.ParentID
  951. }
  952. }
  953. // Channel
  954. for _, item := range config.Channels {
  955. // Single Channel Config
  956. if subjectID == item.ChannelID || isNestedMessage(m, item.ChannelID) {
  957. return item
  958. }
  959. // Multi-Channel Config
  960. if item.ChannelIDs != nil {
  961. for _, subchannel := range *item.ChannelIDs {
  962. if subjectID == subchannel || isNestedMessage(m, subchannel) {
  963. return item
  964. }
  965. }
  966. }
  967. }
  968. // Category Config
  969. for _, item := range config.Categories {
  970. if item.CategoryID != "" {
  971. channel, err := bot.State.Channel(subjectID)
  972. if err == nil {
  973. if channel.ParentID == item.CategoryID {
  974. return item
  975. }
  976. }
  977. }
  978. // Multi-Category Config
  979. if item.CategoryIDs != nil {
  980. for _, subcategory := range *item.CategoryIDs {
  981. channel, err := bot.State.Channel(subjectID)
  982. if err == nil {
  983. if channel.ParentID == subcategory {
  984. if item.CategoryBlacklist != nil {
  985. if stringInSlice(channel.ParentID, *item.CategoryBlacklist) {
  986. return emptyConfig
  987. }
  988. }
  989. return item
  990. }
  991. }
  992. }
  993. }
  994. }
  995. // Server
  996. for _, item := range config.Servers {
  997. if item.ServerID != "" {
  998. guild, err := bot.State.Guild(item.ServerID)
  999. if err == nil {
  1000. for _, channel := range guild.Channels {
  1001. if subjectID == channel.ID || isNestedMessage(m, channel.ID) {
  1002. // Channel Blacklisting within Server
  1003. if item.ServerBlacklist != nil {
  1004. if stringInSlice(subjectID, *item.ServerBlacklist) {
  1005. return emptyConfig
  1006. }
  1007. // Categories
  1008. if channel.ParentID != "" {
  1009. if stringInSlice(channel.ParentID, *item.ServerBlacklist) {
  1010. return emptyConfig
  1011. }
  1012. }
  1013. }
  1014. return item
  1015. }
  1016. }
  1017. }
  1018. }
  1019. // Multi-Server Config
  1020. if item.ServerIDs != nil {
  1021. for _, subserver := range *item.ServerIDs {
  1022. guild, err := bot.State.Guild(subserver)
  1023. if err == nil {
  1024. for _, channel := range guild.Channels {
  1025. if subjectID == channel.ID || isNestedMessage(m, channel.ID) {
  1026. // Channel Blacklisting within Servers
  1027. if item.ServerBlacklist != nil {
  1028. if stringInSlice(subjectID, *item.ServerBlacklist) {
  1029. return emptyConfig
  1030. }
  1031. // Categories
  1032. if channel.ParentID != "" {
  1033. if stringInSlice(channel.ParentID, *item.ServerBlacklist) {
  1034. return emptyConfig
  1035. }
  1036. }
  1037. }
  1038. return item
  1039. }
  1040. }
  1041. }
  1042. }
  1043. }
  1044. }
  1045. // User Config
  1046. for _, item := range config.Users {
  1047. if item.UserID != "" {
  1048. if m.Author.ID == item.UserID {
  1049. return item
  1050. }
  1051. }
  1052. // Multi-User Config
  1053. if item.UserIDs != nil {
  1054. for _, subuser := range *item.UserIDs {
  1055. if m.Author.ID == subuser {
  1056. return item
  1057. }
  1058. }
  1059. }
  1060. }
  1061. // All
  1062. if config.All != nil {
  1063. if config.AllBlacklistChannels != nil {
  1064. if stringInSlice(subjectID, *config.AllBlacklistChannels) {
  1065. return emptyConfig
  1066. }
  1067. }
  1068. if config.AllBlacklistCategories != nil {
  1069. chinf, err := bot.State.Channel(subjectID)
  1070. if err == nil {
  1071. if stringInSlice(chinf.ParentID, *config.AllBlacklistCategories) || stringInSlice(subjectID, *config.AllBlacklistCategories) {
  1072. return emptyConfig
  1073. }
  1074. }
  1075. }
  1076. if config.AllBlacklistServers != nil {
  1077. if stringInSlice(m.GuildID, *config.AllBlacklistServers) {
  1078. return emptyConfig
  1079. }
  1080. }
  1081. if config.AllBlacklistUsers != nil && m.Author != nil {
  1082. if stringInSlice(m.Author.ID, *config.AllBlacklistUsers) {
  1083. return emptyConfig
  1084. }
  1085. }
  1086. return *config.All
  1087. }
  1088. return emptyConfig
  1089. }
  1090. func isAdminChannelRegistered(ChannelID string) bool {
  1091. if config.AdminChannels != nil {
  1092. for _, item := range config.AdminChannels {
  1093. // Single Channel Config
  1094. if ChannelID == item.ChannelID {
  1095. return true
  1096. }
  1097. // Multi-Channel Config
  1098. if item.ChannelIDs != nil {
  1099. if stringInSlice(ChannelID, *item.ChannelIDs) {
  1100. return true
  1101. }
  1102. }
  1103. }
  1104. }
  1105. return false
  1106. }
  1107. func getAdminChannelConfig(ChannelID string) configurationAdminChannel {
  1108. if config.AdminChannels != nil {
  1109. for _, item := range config.AdminChannels {
  1110. // Single Channel Config
  1111. if ChannelID == item.ChannelID {
  1112. return item
  1113. }
  1114. // Multi-Channel Config
  1115. if item.ChannelIDs != nil {
  1116. for _, subchannel := range *item.ChannelIDs {
  1117. if ChannelID == subchannel {
  1118. return item
  1119. }
  1120. }
  1121. }
  1122. }
  1123. }
  1124. return configurationAdminChannel{}
  1125. }
  1126. func isCommandableChannel(m *discordgo.Message) bool {
  1127. if config.AllowGeneralCommands {
  1128. return true
  1129. }
  1130. if isAdminChannelRegistered(m.ChannelID) {
  1131. return true
  1132. } else if channelConfig := getSource(m, nil); channelConfig != emptyConfig {
  1133. if *channelConfig.AllowCommands || isBotAdmin(m) || m.Author.ID == bot.State.User.ID {
  1134. return true
  1135. }
  1136. }
  1137. return false
  1138. }
  1139. func getBoundUsers() []string {
  1140. var users []string
  1141. for _, item := range config.Users {
  1142. if item.UserID != "" {
  1143. if !stringInSlice(item.UserID, users) {
  1144. users = append(users, item.UserID)
  1145. }
  1146. } else if item.UserIDs != nil {
  1147. for _, subuser := range *item.UserIDs {
  1148. if subuser != "" {
  1149. if !stringInSlice(subuser, users) {
  1150. users = append(users, subuser)
  1151. }
  1152. }
  1153. }
  1154. }
  1155. }
  1156. return users
  1157. }
  1158. func getBoundUsersCount() int {
  1159. return len(getBoundUsers())
  1160. }
  1161. func getBoundServers() []string {
  1162. var servers []string
  1163. for _, item := range config.Servers {
  1164. if item.ServerID != "" {
  1165. if !stringInSlice(item.ServerID, servers) {
  1166. servers = append(servers, item.ServerID)
  1167. }
  1168. } else if item.ServerIDs != nil {
  1169. for _, subserver := range *item.ServerIDs {
  1170. if subserver != "" {
  1171. if !stringInSlice(subserver, servers) {
  1172. servers = append(servers, subserver)
  1173. }
  1174. }
  1175. }
  1176. }
  1177. }
  1178. return servers
  1179. }
  1180. func getBoundServersCount() int {
  1181. return len(getBoundServers())
  1182. }
  1183. func getBoundChannels() []string {
  1184. var channels []string
  1185. for _, item := range config.Channels {
  1186. if item.ChannelID != "" {
  1187. if !stringInSlice(item.ChannelID, channels) {
  1188. channels = append(channels, item.ChannelID)
  1189. }
  1190. } else if item.ChannelIDs != nil {
  1191. for _, subchannel := range *item.ChannelIDs {
  1192. if subchannel != "" {
  1193. if !stringInSlice(subchannel, channels) {
  1194. channels = append(channels, subchannel)
  1195. }
  1196. }
  1197. }
  1198. }
  1199. }
  1200. return channels
  1201. }
  1202. func getBoundChannelsCount() int {
  1203. return len(getBoundChannels())
  1204. }
  1205. func getBoundCategories() []string {
  1206. var categories []string
  1207. for _, item := range config.Categories {
  1208. if item.CategoryID != "" {
  1209. if !stringInSlice(item.CategoryID, categories) {
  1210. categories = append(categories, item.CategoryID)
  1211. }
  1212. } else if item.CategoryIDs != nil {
  1213. for _, subcategory := range *item.CategoryIDs {
  1214. if subcategory != "" {
  1215. if !stringInSlice(subcategory, categories) {
  1216. categories = append(categories, subcategory)
  1217. }
  1218. }
  1219. }
  1220. }
  1221. }
  1222. return categories
  1223. }
  1224. func getBoundCategoriesCount() int {
  1225. return len(getBoundCategories())
  1226. }
  1227. func getAllRegisteredChannels() []string {
  1228. var channels []string
  1229. if config.All != nil { // ALL MODE
  1230. for _, guild := range bot.State.Guilds {
  1231. if config.AllBlacklistServers != nil {
  1232. if stringInSlice(guild.ID, *config.AllBlacklistServers) {
  1233. continue
  1234. }
  1235. }
  1236. for _, channel := range guild.Channels {
  1237. if r := getSource(&discordgo.Message{ChannelID: channel.ID}, nil); r == emptyConfig { // easier than redoing it all but way less efficient, im lazy
  1238. continue
  1239. } else {
  1240. if hasPerms(channel.ID, discordgo.PermissionViewChannel) && hasPerms(channel.ID, discordgo.PermissionReadMessageHistory) {
  1241. channels = append(channels, channel.ID)
  1242. }
  1243. }
  1244. }
  1245. }
  1246. } else { // STANDARD MODE
  1247. // Compile all channels sourced from config servers
  1248. for _, server := range config.Servers {
  1249. if server.ServerIDs != nil {
  1250. for _, subserver := range *server.ServerIDs {
  1251. guild, err := bot.State.Guild(subserver)
  1252. if err == nil {
  1253. for _, channel := range guild.Channels {
  1254. if hasPerms(channel.ID, discordgo.PermissionReadMessageHistory) {
  1255. channels = append(channels, channel.ID)
  1256. }
  1257. }
  1258. }
  1259. }
  1260. } else if isNumeric(server.ServerID) {
  1261. guild, err := bot.State.Guild(server.ServerID)
  1262. if err == nil {
  1263. for _, channel := range guild.Channels {
  1264. if hasPerms(channel.ID, discordgo.PermissionReadMessageHistory) {
  1265. channels = append(channels, channel.ID)
  1266. }
  1267. }
  1268. }
  1269. }
  1270. }
  1271. // Compile all config channels under categories, no practical way to poll these other than checking state.
  1272. for _, guild := range bot.State.Guilds {
  1273. for _, channel := range guild.Channels {
  1274. for _, source := range config.Categories {
  1275. if source.CategoryIDs != nil {
  1276. for _, category := range *source.CategoryIDs {
  1277. if channel.ParentID == category {
  1278. channels = append(channels, channel.ID)
  1279. }
  1280. }
  1281. } else if isNumeric(source.CategoryID) {
  1282. if channel.ParentID == source.CategoryID {
  1283. channels = append(channels, channel.ID)
  1284. }
  1285. }
  1286. }
  1287. }
  1288. }
  1289. // Compile all config channels
  1290. for _, channel := range config.Channels {
  1291. if channel.ChannelIDs != nil {
  1292. channels = append(channels, *channel.ChannelIDs...)
  1293. } else if isNumeric(channel.ChannelID) {
  1294. channels = append(channels, channel.ChannelID)
  1295. }
  1296. }
  1297. }
  1298. return channels
  1299. }
  1300. //#endregion