config.go 48 KB

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