common.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "os"
  8. "path"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/bwmarrin/discordgo"
  14. "github.com/fatih/color"
  15. "github.com/hako/durafmt"
  16. "github.com/hashicorp/go-version"
  17. )
  18. //#region Instance
  19. func uptime() time.Duration {
  20. return time.Since(startTime) //.Truncate(time.Second)
  21. }
  22. func properExit() {
  23. // Not formatting string because I only want the exit message to be red.
  24. log.Println(lg("Main", "", color.HiRedString, "[EXIT IN 15 SECONDS] Uptime was %s...", durafmt.Parse(time.Since(startTime)).String()))
  25. log.Println(color.HiCyanString("--------------------------------------------------------------------------------"))
  26. time.Sleep(15 * time.Second)
  27. os.Exit(1)
  28. }
  29. //#endregion
  30. //#region Files
  31. var (
  32. pathBlacklist = []string{"/", "\\", "<", ">", ":", "\"", "|", "?", "*"}
  33. )
  34. func clearPath(p string) string {
  35. r := p
  36. for _, key := range pathBlacklist {
  37. r = strings.ReplaceAll(r, key, "")
  38. }
  39. return r
  40. }
  41. func filenameFromURL(inputURL string) string {
  42. base := path.Base(inputURL)
  43. parts := strings.Split(base, "?")
  44. return path.Clean(parts[0])
  45. }
  46. func filepathExtension(filepath string) string {
  47. if strings.Contains(filepath, "?") {
  48. filepath = strings.Split(filepath, "?")[0]
  49. }
  50. filepath = path.Ext(filepath)
  51. return filepath
  52. }
  53. //#endregion
  54. //#region Text Formatting & Querying
  55. func stringInSlice(a string, list []string) bool {
  56. for _, b := range list {
  57. if strings.EqualFold(a, b) {
  58. return true
  59. }
  60. }
  61. return false
  62. }
  63. func formatNumber(n int64) string {
  64. var numberSeparator byte = ','
  65. if config.EuropeanNumbers {
  66. numberSeparator = '.'
  67. }
  68. in := strconv.FormatInt(n, 10)
  69. out := make([]byte, len(in)+(len(in)-2+int(in[0]/'0'))/3)
  70. if in[0] == '-' {
  71. in, out[0] = in[1:], '-'
  72. }
  73. for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 {
  74. out[j] = in[i]
  75. if i == 0 {
  76. return string(out)
  77. }
  78. if k++; k == 3 {
  79. j, k = j-1, 0
  80. out[j] = numberSeparator
  81. }
  82. }
  83. }
  84. func formatNumberShort(x int64) string {
  85. var numberSeparator string = ","
  86. if config.EuropeanNumbers {
  87. numberSeparator = "."
  88. }
  89. var decimalSeparator string = "."
  90. if config.EuropeanNumbers {
  91. decimalSeparator = ","
  92. }
  93. if x > 1000 {
  94. formattedNumber := formatNumber(x)
  95. splitSlice := strings.Split(formattedNumber, numberSeparator)
  96. suffixes := [4]string{"k", "m", "b", "t"}
  97. partCount := len(splitSlice) - 1
  98. var output string
  99. if splitSlice[1][:1] != "0" {
  100. output = fmt.Sprintf("%s%s%s%s", splitSlice[0], decimalSeparator, splitSlice[1][:1], suffixes[partCount-1])
  101. } else {
  102. output = fmt.Sprintf("%s%s", splitSlice[0], suffixes[partCount-1])
  103. }
  104. return output
  105. }
  106. return fmt.Sprint(x)
  107. }
  108. func pluralS(num int) string {
  109. if num == 1 {
  110. return ""
  111. }
  112. return "s"
  113. }
  114. func wrapHyphens(i string, l int) string {
  115. n := i
  116. if len(n) < l {
  117. n = "- " + n + " -"
  118. for len(n) < l {
  119. n = "-" + n + "-"
  120. }
  121. }
  122. return n
  123. }
  124. func wrapHyphensW(i string) string {
  125. return wrapHyphens(i, 80)
  126. }
  127. func stripSymbols(i string) string {
  128. re, err := regexp.Compile(`[^\w]`)
  129. if err != nil {
  130. log.Fatal(err)
  131. }
  132. return re.ReplaceAllString(i, " ")
  133. }
  134. func isNumeric(s string) bool {
  135. _, err := strconv.ParseFloat(s, 64)
  136. return err == nil
  137. }
  138. func isDate(s string) bool {
  139. _, err := time.Parse("2006-01-02", s)
  140. return err == nil
  141. }
  142. func shortenTime(input string) string {
  143. input = strings.ReplaceAll(input, " nanoseconds", "ns")
  144. input = strings.ReplaceAll(input, " nanosecond", "ns")
  145. input = strings.ReplaceAll(input, " microseconds", "μs")
  146. input = strings.ReplaceAll(input, " microsecond", "μs")
  147. input = strings.ReplaceAll(input, " milliseconds", "ms")
  148. input = strings.ReplaceAll(input, " millisecond", "ms")
  149. input = strings.ReplaceAll(input, " seconds", "s")
  150. input = strings.ReplaceAll(input, " second", "s")
  151. input = strings.ReplaceAll(input, " minutes", "m")
  152. input = strings.ReplaceAll(input, " minute", "m")
  153. input = strings.ReplaceAll(input, " hours", "h")
  154. input = strings.ReplaceAll(input, " hour", "h")
  155. input = strings.ReplaceAll(input, " days", "d")
  156. input = strings.ReplaceAll(input, " day", "d")
  157. input = strings.ReplaceAll(input, " weeks", "w")
  158. input = strings.ReplaceAll(input, " week", "w")
  159. input = strings.ReplaceAll(input, " months", "mo")
  160. input = strings.ReplaceAll(input, " month", "mo")
  161. return input
  162. }
  163. /*func condenseString(input string, length int) string {
  164. filler := "....."
  165. ret := input
  166. if len(input) > length+len(filler) {
  167. half := int((length / 2) - len(filler))
  168. ret = input[0:half] + filler + input[len(input)-half:]
  169. }
  170. return ret
  171. }*/
  172. //#endregion
  173. //#region Github Release Checking
  174. type githubReleaseApiObject struct {
  175. TagName string `json:"tag_name"`
  176. }
  177. var latestGithubRelease string
  178. func getLatestGithubRelease() string {
  179. githubReleaseApiObject := new(githubReleaseApiObject)
  180. err := getJSON("https://api.github.com/repos/"+projectRepoBase+"/releases/latest", githubReleaseApiObject)
  181. if err != nil {
  182. log.Println(lg("API", "Github", color.RedString, "Error fetching current Release JSON: %s", err))
  183. return ""
  184. }
  185. return githubReleaseApiObject.TagName
  186. }
  187. func isLatestGithubRelease() bool {
  188. latestGithubRelease = getLatestGithubRelease()
  189. if latestGithubRelease == "" {
  190. return true
  191. }
  192. thisVersion, err := version.NewVersion(projectVersion)
  193. if err != nil {
  194. log.Println(lg("API", "Github", color.RedString, "Error parsing current version: %s", err))
  195. return true
  196. }
  197. latestVersion, err := version.NewVersion(latestGithubRelease)
  198. if err != nil {
  199. log.Println(lg("API", "Github", color.RedString, "Error parsing latest version: %s", err))
  200. return true
  201. }
  202. if latestVersion.GreaterThan(thisVersion) {
  203. return false
  204. }
  205. return true
  206. }
  207. //#endregion
  208. //#region Requests
  209. func getJSON(url string, target interface{}) error {
  210. r, err := http.Get(url)
  211. if err != nil {
  212. return err
  213. }
  214. defer r.Body.Close()
  215. return json.NewDecoder(r.Body).Decode(target)
  216. }
  217. func getJSONwithHeaders(url string, target interface{}, headers map[string]string) error {
  218. client := &http.Client{}
  219. req, _ := http.NewRequest("GET", url, nil)
  220. for k, v := range headers {
  221. req.Header.Set(k, v)
  222. }
  223. r, err := client.Do(req)
  224. if err != nil {
  225. return err
  226. }
  227. defer r.Body.Close()
  228. return json.NewDecoder(r.Body).Decode(target)
  229. }
  230. //#endregion
  231. //#region Log
  232. /*const (
  233. logLevelOff = -1
  234. logLevelEssential = iota
  235. logLevelFatal
  236. logLevelError
  237. logLevelWarning
  238. logLevelInfo
  239. logLevelDebug
  240. logLevelVerbose
  241. logLevelAll
  242. )*/
  243. func lg(group string, subgroup string, colorFunc func(string, ...interface{}) string, line string, p ...interface{}) string {
  244. colorPrefix := group
  245. switch strings.ToLower(group) {
  246. case "main":
  247. if subgroup == "" {
  248. colorPrefix = ""
  249. } else {
  250. colorPrefix = ""
  251. }
  252. case "debug":
  253. if subgroup == "" {
  254. colorPrefix = color.HiYellowString("[DEBUG]")
  255. } else {
  256. colorPrefix = color.HiYellowString("[DEBUG | %s]", subgroup)
  257. }
  258. case "test":
  259. if subgroup == "" {
  260. colorPrefix = color.HiYellowString("[TEST]")
  261. } else {
  262. colorPrefix = color.HiYellowString("[TEST | %s]", subgroup)
  263. }
  264. case "info":
  265. if subgroup == "" {
  266. colorPrefix = color.CyanString("[Info]")
  267. } else {
  268. colorPrefix = color.CyanString("[Info | %s]", subgroup)
  269. }
  270. case "version":
  271. colorPrefix = color.HiMagentaString("[Version]")
  272. case "settings":
  273. colorPrefix = color.GreenString("[Settings]")
  274. case "database":
  275. colorPrefix = color.HiYellowString("[Database]")
  276. case "setup":
  277. colorPrefix = color.HiGreenString("[Setup]")
  278. case "checkup":
  279. colorPrefix = color.HiGreenString("[Checkup]")
  280. case "discord":
  281. if subgroup == "" {
  282. colorPrefix = color.HiBlueString("[Discord]")
  283. } else {
  284. colorPrefix = color.HiBlueString("[Discord | %s]", subgroup)
  285. }
  286. case "history":
  287. if subgroup == "" {
  288. colorPrefix = color.HiCyanString("[History]")
  289. } else {
  290. colorPrefix = color.HiCyanString("[History | %s]", subgroup)
  291. }
  292. case "command":
  293. if subgroup == "" {
  294. colorPrefix = color.HiGreenString("[Commands]")
  295. } else {
  296. colorPrefix = color.HiGreenString("[Command : %s]", subgroup)
  297. }
  298. case "download":
  299. if subgroup == "" {
  300. colorPrefix = color.GreenString("[Downloads]")
  301. } else {
  302. colorPrefix = color.GreenString("[Downloads | %s]", subgroup)
  303. }
  304. case "message":
  305. if subgroup == "" {
  306. colorPrefix = color.CyanString("[Messages]")
  307. } else {
  308. colorPrefix = color.CyanString("[Messages | %s]", subgroup)
  309. }
  310. case "regex":
  311. if subgroup == "" {
  312. colorPrefix = color.YellowString("[Regex]")
  313. } else {
  314. colorPrefix = color.YellowString("[Regex | %s]", subgroup)
  315. }
  316. case "api":
  317. if subgroup == "" {
  318. colorPrefix = color.HiMagentaString("[APIs]")
  319. } else {
  320. colorPrefix = color.HiMagentaString("[API | %s]", subgroup)
  321. }
  322. }
  323. if bot != nil && botReady {
  324. simplePrefix := group
  325. if subgroup != "" {
  326. simplePrefix += ":" + subgroup
  327. }
  328. for _, adminChannel := range config.AdminChannels {
  329. if *adminChannel.LogProgram {
  330. outputToChannel := func(channel string) {
  331. if channel != "" {
  332. if hasPerms(channel, discordgo.PermissionSendMessages) {
  333. if _, err := bot.ChannelMessageSend(channel,
  334. fmt.Sprintf("```%s | [%s] %s```",
  335. time.Now().Format(time.RFC3339), simplePrefix, fmt.Sprintf(line, p...)),
  336. ); err != nil {
  337. log.Println(color.HiRedString("Failed to send message...\t%s", err))
  338. }
  339. }
  340. }
  341. }
  342. outputToChannel(adminChannel.ChannelID)
  343. if adminChannel.ChannelIDs != nil {
  344. for _, ch := range *adminChannel.ChannelIDs {
  345. outputToChannel(ch)
  346. }
  347. }
  348. }
  349. }
  350. }
  351. pp := "> " // prefix prefix :)
  352. if strings.ToLower(group) == "debug" || strings.ToLower(subgroup) == "debug" {
  353. pp = color.YellowString("? ")
  354. }
  355. if colorPrefix != "" {
  356. colorPrefix += " "
  357. }
  358. return "\t" + pp + colorPrefix + colorFunc(line, p...)
  359. }
  360. //#endregion