config.go 52 KB

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