config.go 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452
  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.Type == discordgo.ChannelTypeGuildPublicThread ||
  950. c.Type == discordgo.ChannelTypeGuildPrivateThread ||
  951. c.Type == discordgo.ChannelTypeGuildNewsThread) && c.ParentID != "" {
  952. subjectID = c.ParentID
  953. }
  954. }
  955. // Channel
  956. for _, item := range config.Channels {
  957. // Single Channel Config
  958. if subjectID == item.ChannelID || isNestedMessage(m, item.ChannelID) {
  959. return item
  960. }
  961. // Multi-Channel Config
  962. if item.ChannelIDs != nil {
  963. for _, subchannel := range *item.ChannelIDs {
  964. if subjectID == subchannel || isNestedMessage(m, subchannel) {
  965. return item
  966. }
  967. }
  968. }
  969. }
  970. // Category Config
  971. for _, item := range config.Categories {
  972. if item.CategoryID != "" {
  973. channel, err := bot.State.Channel(subjectID)
  974. if err != nil {
  975. channel, err = bot.Channel(subjectID)
  976. }
  977. if err == nil {
  978. if channel.ParentID == item.CategoryID {
  979. return item
  980. }
  981. }
  982. }
  983. // Multi-Category Config
  984. if item.CategoryIDs != nil {
  985. for _, subcategory := range *item.CategoryIDs {
  986. channel, err := bot.State.Channel(subjectID)
  987. if err != nil {
  988. channel, err = bot.Channel(subjectID)
  989. }
  990. if err == nil {
  991. if channel.ParentID == subcategory {
  992. if item.CategoryBlacklist != nil {
  993. if stringInSlice(channel.ParentID, *item.CategoryBlacklist) {
  994. return emptyConfig
  995. }
  996. }
  997. return item
  998. }
  999. }
  1000. }
  1001. }
  1002. }
  1003. // Server
  1004. for _, item := range config.Servers {
  1005. if item.ServerID != "" {
  1006. guild, err := bot.State.Guild(item.ServerID)
  1007. if err != nil {
  1008. guild, err = bot.Guild(item.ServerID)
  1009. }
  1010. if err == nil {
  1011. for _, channel := range guild.Channels {
  1012. if subjectID == channel.ID || isNestedMessage(m, channel.ID) {
  1013. // Channel Blacklisting within Server
  1014. if item.ServerBlacklist != nil {
  1015. if stringInSlice(subjectID, *item.ServerBlacklist) {
  1016. return emptyConfig
  1017. }
  1018. // Categories
  1019. if channel.ParentID != "" {
  1020. if stringInSlice(channel.ParentID, *item.ServerBlacklist) {
  1021. return emptyConfig
  1022. }
  1023. }
  1024. }
  1025. return item
  1026. }
  1027. }
  1028. }
  1029. }
  1030. // Multi-Server Config
  1031. if item.ServerIDs != nil {
  1032. for _, subserver := range *item.ServerIDs {
  1033. guild, err := bot.State.Guild(subserver)
  1034. if err != nil {
  1035. guild, err = bot.Guild(subserver)
  1036. }
  1037. if err == nil {
  1038. for _, channel := range guild.Channels {
  1039. if subjectID == channel.ID || isNestedMessage(m, channel.ID) {
  1040. // Channel Blacklisting within Servers
  1041. if item.ServerBlacklist != nil {
  1042. if stringInSlice(subjectID, *item.ServerBlacklist) {
  1043. return emptyConfig
  1044. }
  1045. // Categories
  1046. if channel.ParentID != "" {
  1047. if stringInSlice(channel.ParentID, *item.ServerBlacklist) {
  1048. return emptyConfig
  1049. }
  1050. }
  1051. }
  1052. return item
  1053. }
  1054. }
  1055. }
  1056. }
  1057. }
  1058. }
  1059. // User Config
  1060. for _, item := range config.Users {
  1061. if item.UserID != "" {
  1062. if m.Author.ID == item.UserID {
  1063. return item
  1064. }
  1065. }
  1066. // Multi-User Config
  1067. if item.UserIDs != nil {
  1068. for _, subuser := range *item.UserIDs {
  1069. if m.Author.ID == subuser {
  1070. return item
  1071. }
  1072. }
  1073. }
  1074. }
  1075. // All
  1076. if config.All != nil {
  1077. if config.AllBlacklistChannels != nil {
  1078. if stringInSlice(subjectID, *config.AllBlacklistChannels) {
  1079. return emptyConfig
  1080. }
  1081. }
  1082. if config.AllBlacklistCategories != nil {
  1083. chinf, err := bot.State.Channel(subjectID)
  1084. if err == nil {
  1085. if stringInSlice(chinf.ParentID, *config.AllBlacklistCategories) || stringInSlice(subjectID, *config.AllBlacklistCategories) {
  1086. return emptyConfig
  1087. }
  1088. }
  1089. }
  1090. if config.AllBlacklistServers != nil {
  1091. if stringInSlice(m.GuildID, *config.AllBlacklistServers) {
  1092. return emptyConfig
  1093. }
  1094. }
  1095. if config.AllBlacklistUsers != nil && m.Author != nil {
  1096. if stringInSlice(m.Author.ID, *config.AllBlacklistUsers) {
  1097. return emptyConfig
  1098. }
  1099. }
  1100. return *config.All
  1101. }
  1102. return emptyConfig
  1103. }
  1104. func isAdminChannelRegistered(ChannelID string) bool {
  1105. if config.AdminChannels != nil {
  1106. for _, item := range config.AdminChannels {
  1107. // Single Channel Config
  1108. if ChannelID == item.ChannelID {
  1109. return true
  1110. }
  1111. // Multi-Channel Config
  1112. if item.ChannelIDs != nil {
  1113. if stringInSlice(ChannelID, *item.ChannelIDs) {
  1114. return true
  1115. }
  1116. }
  1117. }
  1118. }
  1119. return false
  1120. }
  1121. func getAdminChannelConfig(ChannelID string) configurationAdminChannel {
  1122. if config.AdminChannels != nil {
  1123. for _, item := range config.AdminChannels {
  1124. // Single Channel Config
  1125. if ChannelID == item.ChannelID {
  1126. return item
  1127. }
  1128. // Multi-Channel Config
  1129. if item.ChannelIDs != nil {
  1130. for _, subchannel := range *item.ChannelIDs {
  1131. if ChannelID == subchannel {
  1132. return item
  1133. }
  1134. }
  1135. }
  1136. }
  1137. }
  1138. return configurationAdminChannel{}
  1139. }
  1140. func isCommandableChannel(m *discordgo.Message) bool {
  1141. if config.AllowGeneralCommands {
  1142. return true
  1143. }
  1144. if isAdminChannelRegistered(m.ChannelID) {
  1145. return true
  1146. } else if channelConfig := getSource(m, nil); channelConfig != emptyConfig {
  1147. if *channelConfig.AllowCommands || isBotAdmin(m) || m.Author.ID == bot.State.User.ID {
  1148. return true
  1149. }
  1150. }
  1151. return false
  1152. }
  1153. func getBoundUsers() []string {
  1154. var users []string
  1155. for _, item := range config.Users {
  1156. if item.UserID != "" {
  1157. if !stringInSlice(item.UserID, users) {
  1158. users = append(users, item.UserID)
  1159. }
  1160. } else if item.UserIDs != nil {
  1161. for _, subuser := range *item.UserIDs {
  1162. if subuser != "" {
  1163. if !stringInSlice(subuser, users) {
  1164. users = append(users, subuser)
  1165. }
  1166. }
  1167. }
  1168. }
  1169. }
  1170. return users
  1171. }
  1172. func getBoundUsersCount() int {
  1173. return len(getBoundUsers())
  1174. }
  1175. func getBoundServers() []string {
  1176. var servers []string
  1177. for _, item := range config.Servers {
  1178. if item.ServerID != "" {
  1179. if !stringInSlice(item.ServerID, servers) {
  1180. servers = append(servers, item.ServerID)
  1181. }
  1182. } else if item.ServerIDs != nil {
  1183. for _, subserver := range *item.ServerIDs {
  1184. if subserver != "" {
  1185. if !stringInSlice(subserver, servers) {
  1186. servers = append(servers, subserver)
  1187. }
  1188. }
  1189. }
  1190. }
  1191. }
  1192. return servers
  1193. }
  1194. func getBoundServersCount() int {
  1195. return len(getBoundServers())
  1196. }
  1197. func getBoundChannels() []string {
  1198. var channels []string
  1199. for _, item := range config.Channels {
  1200. if item.ChannelID != "" {
  1201. if !stringInSlice(item.ChannelID, channels) {
  1202. channels = append(channels, item.ChannelID)
  1203. }
  1204. } else if item.ChannelIDs != nil {
  1205. for _, subchannel := range *item.ChannelIDs {
  1206. if subchannel != "" {
  1207. if !stringInSlice(subchannel, channels) {
  1208. channels = append(channels, subchannel)
  1209. }
  1210. }
  1211. }
  1212. }
  1213. }
  1214. return channels
  1215. }
  1216. func getBoundChannelsCount() int {
  1217. return len(getBoundChannels())
  1218. }
  1219. func getBoundCategories() []string {
  1220. var categories []string
  1221. for _, item := range config.Categories {
  1222. if item.CategoryID != "" {
  1223. if !stringInSlice(item.CategoryID, categories) {
  1224. categories = append(categories, item.CategoryID)
  1225. }
  1226. } else if item.CategoryIDs != nil {
  1227. for _, subcategory := range *item.CategoryIDs {
  1228. if subcategory != "" {
  1229. if !stringInSlice(subcategory, categories) {
  1230. categories = append(categories, subcategory)
  1231. }
  1232. }
  1233. }
  1234. }
  1235. }
  1236. return categories
  1237. }
  1238. func getBoundCategoriesCount() int {
  1239. return len(getBoundCategories())
  1240. }
  1241. func getAllRegisteredChannels() []string {
  1242. var channels []string
  1243. if config.All != nil { // ALL MODE
  1244. for _, guild := range bot.State.Guilds {
  1245. if config.AllBlacklistServers != nil {
  1246. if stringInSlice(guild.ID, *config.AllBlacklistServers) {
  1247. continue
  1248. }
  1249. }
  1250. for _, channel := range guild.Channels {
  1251. if r := getSource(&discordgo.Message{ChannelID: channel.ID}, nil); r == emptyConfig { // easier than redoing it all but way less efficient, im lazy
  1252. continue
  1253. } else {
  1254. if hasPerms(channel.ID, discordgo.PermissionViewChannel) && hasPerms(channel.ID, discordgo.PermissionReadMessageHistory) {
  1255. channels = append(channels, channel.ID)
  1256. }
  1257. }
  1258. }
  1259. }
  1260. } else { // STANDARD MODE
  1261. // Compile all channels sourced from config servers
  1262. for _, server := range config.Servers {
  1263. if server.ServerIDs != nil {
  1264. for _, subserver := range *server.ServerIDs {
  1265. guild, err := bot.State.Guild(subserver)
  1266. if err == nil {
  1267. for _, channel := range guild.Channels {
  1268. if hasPerms(channel.ID, discordgo.PermissionReadMessageHistory) {
  1269. channels = append(channels, channel.ID)
  1270. }
  1271. }
  1272. }
  1273. }
  1274. } else if isNumeric(server.ServerID) {
  1275. guild, err := bot.State.Guild(server.ServerID)
  1276. if err == nil {
  1277. for _, channel := range guild.Channels {
  1278. if hasPerms(channel.ID, discordgo.PermissionReadMessageHistory) {
  1279. channels = append(channels, channel.ID)
  1280. }
  1281. }
  1282. }
  1283. }
  1284. }
  1285. // Compile all config channels under categories, no practical way to poll these other than checking state.
  1286. for _, guild := range bot.State.Guilds {
  1287. for _, channel := range guild.Channels {
  1288. for _, source := range config.Categories {
  1289. if source.CategoryIDs != nil {
  1290. for _, category := range *source.CategoryIDs {
  1291. if channel.ParentID == category {
  1292. channels = append(channels, channel.ID)
  1293. }
  1294. }
  1295. } else if isNumeric(source.CategoryID) {
  1296. if channel.ParentID == source.CategoryID {
  1297. channels = append(channels, channel.ID)
  1298. }
  1299. }
  1300. }
  1301. }
  1302. }
  1303. // Compile all config channels
  1304. for _, channel := range config.Channels {
  1305. if channel.ChannelIDs != nil {
  1306. channels = append(channels, *channel.ChannelIDs...)
  1307. } else if isNumeric(channel.ChannelID) {
  1308. channels = append(channels, channel.ChannelID)
  1309. }
  1310. }
  1311. }
  1312. return channels
  1313. }
  1314. //#endregion