Compare commits
No commits in common. "main" and "rewrite-go" have entirely different histories.
main
...
rewrite-go
50 changed files with 3049 additions and 2234 deletions
31
.eslintrc.js
31
.eslintrc.js
|
@ -1,31 +0,0 @@
|
||||||
const OFF = 0;
|
|
||||||
// const WARN = 1;
|
|
||||||
const ERROR = 2;
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
extends: ["eslint:recommended"],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
es6: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
indent: OFF,
|
|
||||||
semi: ERROR,
|
|
||||||
quotes: [ERROR, "double", {avoidEscape: true, allowTemplateLiterals: true}],
|
|
||||||
"no-empty": ERROR,
|
|
||||||
"array-callback-return": ERROR,
|
|
||||||
"consistent-return": ERROR,
|
|
||||||
eqeqeq: OFF,
|
|
||||||
"prefer-const": ERROR,
|
|
||||||
"no-unused-vars": [ERROR, {args: "none", varsIgnorePattern: "^_"}],
|
|
||||||
"no-console": OFF,
|
|
||||||
"no-debugger": OFF,
|
|
||||||
"require-atomic-updates": OFF,
|
|
||||||
},
|
|
||||||
globals: {
|
|
||||||
comcord: true,
|
|
||||||
},
|
|
||||||
};
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
node_modules/
|
comcord
|
||||||
|
comcord.exe
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"semi": true,
|
|
||||||
"bracketSpacing": false,
|
|
||||||
"endOfLine": "lf"
|
|
||||||
}
|
|
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 Cynthia Foxwell
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
70
README.md
70
README.md
|
@ -1,4 +1,4 @@
|
||||||
# comcord
|
# comcord (`rewrite-go`)
|
||||||
A CLI-based client for Discord inspired by [SDF](https://sdf.org)'s [commode](https://sdf.org/?tutorials/comnotirc).
|
A CLI-based client for Discord inspired by [SDF](https://sdf.org)'s [commode](https://sdf.org/?tutorials/comnotirc).
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
@ -6,30 +6,13 @@ A CLI-based client for Discord inspired by [SDF](https://sdf.org)'s [commode](ht
|
||||||
2. I've been spending more time in commode on SDF and have been accustomed to the experience.
|
2. I've been spending more time in commode on SDF and have been accustomed to the experience.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
1. `pnpm i`
|
TODO
|
||||||
2. `node src/index.js <token>`
|
|
||||||
|
|
||||||
Your token will be then stored in `.comcordrc` after the first launch.
|
## Rewrite Design Decisions
|
||||||
|
Go is more portable than Node.js
|
||||||
### User Accounts
|
|
||||||
User accounts are _partially_ supported via `allowUserAccounts=true` in your `.comcordrc`.
|
|
||||||
This is use at your own risk, despite spoofing the official client. I am not responsible for any banned accounts.
|
|
||||||
|
|
||||||
#### Guild members not populating
|
|
||||||
This is due to most libraries not implementing Lazy Guilds, as bots do not need lazy guilds to function.
|
|
||||||
|
|
||||||
If you are willing to implement Lazy Guilds based off of [unofficial documentation](https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html)
|
|
||||||
and my already existing horrible hacks to make user accounts work in the first place, feel free to send a PR (on GitLab, GitHub repo is a read only mirror).
|
|
||||||
|
|
||||||
### Bot Accounts (prefered)
|
|
||||||
You **MUST** grant your bot all Privileged Gateway Intents.
|
|
||||||
|
|
||||||
## Design Decisions
|
|
||||||
- Node.js was chosen currently due to familiarity.
|
|
||||||
- Dysnomia was chosen due to familiarity and the nature of everything not being abstracted out to 200 different classes unlike discord.js.
|
|
||||||
- "Jank" by design. While I don't expect anyone to actually use comcord on serial terminals or teletypes other than for meme factor, the option is still there.
|
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
- [x] Send mode
|
||||||
- [x] Commands
|
- [x] Commands
|
||||||
- [x] Quit (q)
|
- [x] Quit (q)
|
||||||
- [x] Switch guilds (G)
|
- [x] Switch guilds (G)
|
||||||
|
@ -38,41 +21,42 @@ You **MUST** grant your bot all Privileged Gateway Intents.
|
||||||
- [x] Emote (e)
|
- [x] Emote (e)
|
||||||
- Just sends message surrounded in `*`'s
|
- Just sends message surrounded in `*`'s
|
||||||
- [ ] Finger (f)
|
- [ ] Finger (f)
|
||||||
- [ ] Shows presence data if available
|
- Shows presence data if available
|
||||||
- [ ] Creation date, join date, ID, etc
|
- Creation date, join date, ID, etc
|
||||||
- [x] Room history (r)
|
- [x] Room history (r)
|
||||||
- [x] Extended room history (R)
|
- [x] Extended room history (R)
|
||||||
|
- [x] Peek (p)
|
||||||
|
- [x] Cross-guild peek (P)
|
||||||
- [x] List channels (l)
|
- [x] List channels (l)
|
||||||
- [x] List guilds (L)
|
- [x] List guilds (L)
|
||||||
- [x] Clear (c)
|
- [x] Clear (c)
|
||||||
- [ ] Surf channels forwards (>)
|
- [ ] Surf channels forwards (>)
|
||||||
- [ ] Surf channels backwards (<)
|
- [ ] Surf channels backwards (<)
|
||||||
- [x] AFK toggle (A)
|
- [ ] AFK toggle (A)
|
||||||
- [x] Send DM (s)
|
- [ ] Send DM (s)
|
||||||
- [x] Answer DM (a)
|
- [ ] Answer DM (a)
|
||||||
- [x] Peek (p)
|
- [x] Current time (+)
|
||||||
|
- [ ] DM history (TBD)
|
||||||
|
- [ ] Reply to message (TBD)
|
||||||
|
- [ ] Toggle color (z)
|
||||||
- [x] Message Receiving
|
- [x] Message Receiving
|
||||||
- [x] Markdown styling
|
- Markdown styling
|
||||||
- [x] Common markdown (bold, italic, etc)
|
- [x] Emotes
|
||||||
- [x] Figure out how spoilers would work
|
|
||||||
- [x] Emotes?????
|
|
||||||
- [x] Timestamp parsing
|
- [x] Timestamp parsing
|
||||||
- [x] Mentions parsing
|
- [x] Mentions parsing
|
||||||
- [ ] Embeds in the style of commode's posted links
|
- [ ] Embeds
|
||||||
|
- [ ] Plain links with title = commode's posted links
|
||||||
- [x] Messages wrapped in `*`'s or `_`'s parsed as emotes
|
- [x] Messages wrapped in `*`'s or `_`'s parsed as emotes
|
||||||
- [x] Inline DMs to replicate commode's private messages
|
- [x] Inline DMs to replicate commode's private messages
|
||||||
- [x] Replies
|
- [x] Replies
|
||||||
|
- [ ] Group DMs
|
||||||
|
- [ ] Only works with user accounts, might not even be worth doing
|
||||||
- [x] Message sending
|
- [x] Message sending
|
||||||
- [x] Puts incoming messages into queue whilst in send mode
|
- [x] Puts incoming messages into queue whilst in send mode
|
||||||
- [ ] Mentions
|
- [x] Send typing
|
||||||
- [ ] Replies
|
- [ ] Mentioning
|
||||||
- [x] Configuration
|
- [x] Configuration
|
||||||
|
- [x] Write token from argv into rc file if rc file doesn't exist
|
||||||
- [x] Default guild/channel
|
- [x] Default guild/channel
|
||||||
- No way to set in client (yet?), `defaultChannel=` and `defaultGuild=` in your `.comcordrc`.
|
- [ ] Threads/Forums
|
||||||
- [ ] Threads
|
- [ ] External rich presence when using bot accounts
|
||||||
- [x] Not have the token just be in argv
|
|
||||||
- [x] Not have everything in one file
|
|
||||||
|
|
||||||
## Repository
|
|
||||||
If you're viewing this on GitHub or GitLab, you are viewing a read only mirror.
|
|
||||||
The main repository is located on [Gitdab](https://gitdab.com/Cynosphere/comcord) and is push mirrored to the other two.
|
|
||||||
|
|
9
commands/clear.go
Normal file
9
commands/clear.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ClearCommand() {
|
||||||
|
fmt.Print("\n\r\033[H\033[2J")
|
||||||
|
}
|
45
commands/emote.go
Normal file
45
commands/emote.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/lib"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EmoteCommand() {
|
||||||
|
channelId := state.GetCurrentChannel()
|
||||||
|
if channelId == "" {
|
||||||
|
fmt.Print("<not in a channel>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt := ":emote> "
|
||||||
|
lib.MakePrompt(prompt, true, func(input string, interrupt bool) {
|
||||||
|
if input == "" {
|
||||||
|
if interrupt {
|
||||||
|
fmt.Print("^C<no message sent>\n\r")
|
||||||
|
} else {
|
||||||
|
fmt.Print(prompt, "<no message sent>\n\r")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Print(prompt, input, "\n\r")
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
snowflake, err := discord.ParseSnowflake(channelId)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.SendMessage(discord.ChannelID(snowflake), "*" + input + "*")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to send message: ", err.Error(), ">\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: update afk state
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
658
commands/guild.go
Normal file
658
commands/guild.go
Normal file
|
@ -0,0 +1,658 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/lib"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
tsize "github.com/kopoli/go-terminal-size"
|
||||||
|
"github.com/mgutz/ansi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`)
|
||||||
|
|
||||||
|
type GuildListing struct {
|
||||||
|
Name string
|
||||||
|
Members int
|
||||||
|
Online int
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListGuildsCommand() {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
longest := 0
|
||||||
|
guilds := make([]GuildListing, 0)
|
||||||
|
|
||||||
|
clientGuilds, err := client.Guilds()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get guilds: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, guild := range clientGuilds {
|
||||||
|
length := utf8.RuneCountInString(guild.Name)
|
||||||
|
if length > longest {
|
||||||
|
longest = length
|
||||||
|
}
|
||||||
|
|
||||||
|
withCount, err := client.GuildWithCount(guild.ID)
|
||||||
|
if err == nil {
|
||||||
|
guilds = append(guilds, GuildListing{
|
||||||
|
Name: guild.Name,
|
||||||
|
Members: int(withCount.ApproximateMembers),
|
||||||
|
Online: int(withCount.ApproximatePresences),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
guilds = append(guilds, GuildListing{
|
||||||
|
Name: guild.Name,
|
||||||
|
Members: int(guild.ApproximateMembers),
|
||||||
|
Online: int(guild.ApproximatePresences),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
fmt.Printf(" %*s online total\n\r", longest, "guild-name")
|
||||||
|
fmt.Print(strings.Repeat("-", 80) + "\n\r")
|
||||||
|
for _, guild := range guilds {
|
||||||
|
fmt.Printf(" %*s %6d %5d\n\r", longest, guild.Name, guild.Online, guild.Members)
|
||||||
|
}
|
||||||
|
fmt.Print(strings.Repeat("-", 80) + "\n\r")
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSortedChannels(guildId string, withCategories bool, withPrivate bool) []discord.Channel {
|
||||||
|
client := state.GetClient()
|
||||||
|
channels := make([]discord.Channel, 0)
|
||||||
|
|
||||||
|
guildSnowflake, err := discord.ParseSnowflake(guildId)
|
||||||
|
if err != nil {
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedGuildId := discord.GuildID(guildSnowflake)
|
||||||
|
|
||||||
|
guild, err := client.GuildStore.Guild(parsedGuildId)
|
||||||
|
if err != nil {
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
guildChannels, err := client.ChannelStore.Channels(guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
selfMember, err := client.MemberStore.Member(guild.ID, self.ID)
|
||||||
|
if err != nil {
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
if withCategories {
|
||||||
|
categories := make(map[string][]discord.Channel)
|
||||||
|
|
||||||
|
for _, channel := range guildChannels {
|
||||||
|
if channel.Type != discord.GuildText && channel.Type != discord.GuildAnnouncement {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
private := false
|
||||||
|
|
||||||
|
if channel.ParentID.IsValid() {
|
||||||
|
category, err := client.ChannelStore.Channel(channel.ParentID)
|
||||||
|
if err == nil {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *category, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if private {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, channel, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if private && !withPrivate {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryID := "0"
|
||||||
|
if channel.ParentID.IsValid() {
|
||||||
|
categoryID = channel.ParentID.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, has := categories[categoryID]
|
||||||
|
if !has {
|
||||||
|
categories[categoryID] = make([]discord.Channel, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
categories[categoryID] = append(categories[categoryID], channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, category := range categories {
|
||||||
|
// sort channels by position
|
||||||
|
sort.Slice(category, func(i, j int) bool {
|
||||||
|
return category[i].Position < category[j].Position
|
||||||
|
})
|
||||||
|
categoryChannels := make([]discord.Channel, 0)
|
||||||
|
|
||||||
|
// append category channel to top
|
||||||
|
if id != "0" {
|
||||||
|
parsedCategoryId, err := discord.ParseSnowflake(id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, channel := range guildChannels {
|
||||||
|
if channel.ID == discord.ChannelID(parsedCategoryId) {
|
||||||
|
categoryChannels = append(categoryChannels, channel)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// append channels
|
||||||
|
for _, channel := range category {
|
||||||
|
categoryChannels = append(categoryChannels, channel)
|
||||||
|
}
|
||||||
|
categories[id] = categoryChannels
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, 0)
|
||||||
|
for id := range categories {
|
||||||
|
if id == "0" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keys = append(keys, id)
|
||||||
|
}
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
pa, _ := discord.ParseSnowflake(keys[i])
|
||||||
|
pb, _ := discord.ParseSnowflake(keys[j])
|
||||||
|
|
||||||
|
ca, _ := client.ChannelStore.Channel(discord.ChannelID(pa))
|
||||||
|
cb, _ := client.ChannelStore.Channel(discord.ChannelID(pb))
|
||||||
|
|
||||||
|
return ca.Position < cb.Position
|
||||||
|
})
|
||||||
|
sortedCategories := make(map[string][]discord.Channel)
|
||||||
|
sortedCategories["0"] = categories["0"]
|
||||||
|
|
||||||
|
for _, id := range keys {
|
||||||
|
sortedCategories[id] = categories[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, categoryChannels := range sortedCategories {
|
||||||
|
for _, channel := range categoryChannels {
|
||||||
|
channels = append(channels, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, channel := range guildChannels {
|
||||||
|
if channel.Type != discord.GuildText && channel.Type != discord.GuildAnnouncement {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
private := false
|
||||||
|
|
||||||
|
if channel.ParentID.IsValid() {
|
||||||
|
category, err := client.ChannelStore.Channel(channel.ParentID)
|
||||||
|
if err == nil {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *category, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if private {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, channel, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if private && !withPrivate {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
channels = append(channels, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(channels, func(i, j int) bool {
|
||||||
|
return channels[i].Position < channels[j].Position
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListChannelsCommand() {
|
||||||
|
client := state.GetClient()
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get self: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
if currentGuild == "" {
|
||||||
|
fmt.Print("<not in a guild>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guildSnowflake, err := discord.ParseSnowflake(currentGuild)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to parse current guild id: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedGuildId := discord.GuildID(guildSnowflake)
|
||||||
|
guild, err := client.GuildStore.Guild(parsedGuildId)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get current guild: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selfMember, err := client.MemberStore.Member(parsedGuildId, self.ID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get self member: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
longest := 0
|
||||||
|
channels := GetSortedChannels(currentGuild, true, false)
|
||||||
|
|
||||||
|
for _, channel := range channels {
|
||||||
|
private := false
|
||||||
|
|
||||||
|
if channel.ParentID.IsValid() {
|
||||||
|
category, err := client.ChannelStore.Channel(channel.ParentID)
|
||||||
|
if err == nil {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *category, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if private {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, channel, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
category := channel.Type == discord.GuildCategory
|
||||||
|
|
||||||
|
catLen := 0
|
||||||
|
if category {
|
||||||
|
catLen = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
privLen := 0
|
||||||
|
if private {
|
||||||
|
privLen = 1
|
||||||
|
}
|
||||||
|
length := utf8.RuneCountInString(channel.Name) + privLen + catLen
|
||||||
|
|
||||||
|
if length > longest {
|
||||||
|
longest = int(math.Min(25, float64(length)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
fmt.Printf(" %*s created topic\n\r", longest, "channel-name")
|
||||||
|
fmt.Print(strings.Repeat("-", 80) + "\n\r")
|
||||||
|
for _, channel := range channels {
|
||||||
|
private := false
|
||||||
|
|
||||||
|
if channel.ParentID.IsValid() {
|
||||||
|
category, err := client.ChannelStore.Channel(channel.ParentID)
|
||||||
|
if err == nil {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *category, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if private {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, channel, *selfMember)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
category := channel.Type == discord.GuildCategory
|
||||||
|
topic := REGEX_EMOTE.ReplaceAllString(channel.Topic, ":$1:")
|
||||||
|
topic = strings.ReplaceAll(topic, "\n", " ")
|
||||||
|
name := channel.Name
|
||||||
|
if category {
|
||||||
|
name = "-- " + name + " --"
|
||||||
|
}
|
||||||
|
if private {
|
||||||
|
name = "*" + name
|
||||||
|
}
|
||||||
|
|
||||||
|
nameLength := utf8.RuneCountInString(name)
|
||||||
|
if nameLength > 25 {
|
||||||
|
name = name[:24] + "\u2026"
|
||||||
|
}
|
||||||
|
|
||||||
|
topicLength := utf8.RuneCountInString(topic)
|
||||||
|
longestTopic := 80 - (longest + 5) - 11
|
||||||
|
if topicLength > longestTopic {
|
||||||
|
topic = topic[:(longestTopic - 1)] + "\u2026"
|
||||||
|
}
|
||||||
|
|
||||||
|
created := "??-???-??"
|
||||||
|
timestamp := channel.CreatedAt()
|
||||||
|
created = timestamp.UTC().Format("02-Jan-06")
|
||||||
|
|
||||||
|
fmt.Printf(" %*s %s %s\n\r", longest, name, created, topic)
|
||||||
|
}
|
||||||
|
fmt.Print(strings.Repeat("-", 80) + "\n\r")
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListedMember struct {
|
||||||
|
Name string
|
||||||
|
Bot bool
|
||||||
|
Status discord.Status
|
||||||
|
Position int
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListUsersCommand() {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
currentChannel := state.GetCurrentChannel()
|
||||||
|
|
||||||
|
if currentGuild == "" {
|
||||||
|
fmt.Print("<not in a guild>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if currentChannel == "" {
|
||||||
|
fmt.Print("<not in a channel>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedGuildId, err := discord.ParseSnowflake(currentGuild)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to parse guild id: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parsedChannelId, err := discord.ParseSnowflake(currentChannel)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guild, err := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get guild: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get channel: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
longest := 0
|
||||||
|
|
||||||
|
sortedMembers := make([]ListedMember, 0)
|
||||||
|
|
||||||
|
presences, err := client.Presences(guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get presences: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, presence := range presences {
|
||||||
|
if presence.Status == discord.OfflineStatus {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
member, err := client.MemberStore.Member(guild.ID, presence.User.ID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
private := false
|
||||||
|
|
||||||
|
if channel.ParentID.IsValid() {
|
||||||
|
category, err := client.ChannelStore.Channel(channel.ParentID)
|
||||||
|
if err == nil {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *category, *member)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if private {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *channel, *member)
|
||||||
|
private = !perms.Has(discord.PermissionViewChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
length := utf8.RuneCountInString(member.User.Username) + 3
|
||||||
|
if length > longest {
|
||||||
|
longest = length
|
||||||
|
}
|
||||||
|
|
||||||
|
position := 0
|
||||||
|
for _, id := range member.RoleIDs {
|
||||||
|
role, err := client.RoleStore.Role(guild.ID, id)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if role.Hoist && role.Position > position {
|
||||||
|
position = role.Position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedMembers = append(sortedMembers, ListedMember{
|
||||||
|
Name: member.User.Username,
|
||||||
|
Bot: member.User.Bot,
|
||||||
|
Status: presence.Status,
|
||||||
|
Position: position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
fmt.Printf("[you are in '%s' in '#%s' among %d]\n\r", guild.Name, channel.Name, len(sortedMembers))
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
|
||||||
|
membersByPosition := make(map[int][]ListedMember)
|
||||||
|
for _, member := range sortedMembers {
|
||||||
|
_, has := membersByPosition[member.Position]
|
||||||
|
if !has {
|
||||||
|
membersByPosition[member.Position] = make([]ListedMember, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
membersByPosition[member.Position] = append(membersByPosition[member.Position], member)
|
||||||
|
}
|
||||||
|
for _, members := range membersByPosition {
|
||||||
|
sort.Slice(members, func(i, j int) bool {
|
||||||
|
return members[i].Name < members[j].Name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
positions := make([]int, 0, len(membersByPosition))
|
||||||
|
for k := range membersByPosition {
|
||||||
|
positions = append(positions, k)
|
||||||
|
}
|
||||||
|
sort.Slice(positions, func(i, j int) bool {
|
||||||
|
return positions[i] > positions[j]
|
||||||
|
})
|
||||||
|
|
||||||
|
size, err := tsize.GetSize()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
columns := int(math.Floor(float64(size.Width) / float64(longest)))
|
||||||
|
|
||||||
|
index := 0
|
||||||
|
for _, position := range positions {
|
||||||
|
members := membersByPosition[position]
|
||||||
|
if len(members) > 150 {
|
||||||
|
str := "[hiding " + strconv.Itoa(len(members)) + " members]"
|
||||||
|
length := utf8.RuneCountInString(str)
|
||||||
|
|
||||||
|
index++
|
||||||
|
|
||||||
|
pad := 0
|
||||||
|
if index % columns != 0 {
|
||||||
|
pad = longest - length
|
||||||
|
}
|
||||||
|
if pad < 0 {
|
||||||
|
pad = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(str + strings.Repeat(" ", pad))
|
||||||
|
|
||||||
|
if index % columns == 0 {
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, member := range members {
|
||||||
|
|
||||||
|
statusColor := "reset"
|
||||||
|
if member.Status == discord.OnlineStatus {
|
||||||
|
statusColor = "green+b"
|
||||||
|
} else if member.Status == discord.IdleStatus {
|
||||||
|
statusColor = "yellow+b"
|
||||||
|
} else if member.Status == discord.DoNotDisturbStatus {
|
||||||
|
statusColor = "red+b"
|
||||||
|
}
|
||||||
|
|
||||||
|
nameColor := "reset"
|
||||||
|
if member.Bot {
|
||||||
|
nameColor = "yellow"
|
||||||
|
}
|
||||||
|
|
||||||
|
nameAndStatus := ansi.Color(" \u2022 ", statusColor) + ansi.Color(member.Name, nameColor)
|
||||||
|
nameLength := utf8.RuneCountInString(member.Name) + 3
|
||||||
|
|
||||||
|
index++
|
||||||
|
|
||||||
|
pad := 0
|
||||||
|
if index % columns != 0 {
|
||||||
|
pad = longest - nameLength
|
||||||
|
}
|
||||||
|
if pad < 0 {
|
||||||
|
pad = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(nameAndStatus + strings.Repeat(" ", pad))
|
||||||
|
|
||||||
|
if index % columns == 0 {
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index % columns != 0 {
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
|
||||||
|
if channel.Topic != "" {
|
||||||
|
fmt.Print("--Topic" + strings.Repeat("-", 73) + "\n\r")
|
||||||
|
for _, line := range strings.Split(channel.Topic, "\n") {
|
||||||
|
fmt.Print(line + "\n\r")
|
||||||
|
}
|
||||||
|
fmt.Print(strings.Repeat("-", 80) + "\n\r")
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SwitchGuild(input string) {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
if input == "" {
|
||||||
|
ListChannelsCommand()
|
||||||
|
ListUsersCommand()
|
||||||
|
} else {
|
||||||
|
target := ""
|
||||||
|
|
||||||
|
guilds, err := client.GuildStore.Guilds()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get guilds: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, guild := range guilds {
|
||||||
|
if strings.Index(strings.ToLower(guild.Name), strings.ToLower(input)) > -1 {
|
||||||
|
target = guild.ID.String()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target == "" {
|
||||||
|
fmt.Print("<guild not found>\n\r")
|
||||||
|
} else {
|
||||||
|
state.SetCurrentGuild(target)
|
||||||
|
last := state.GetLastChannel(target)
|
||||||
|
if last == "" {
|
||||||
|
channels := GetSortedChannels(target, false, false)
|
||||||
|
if len(channels) > 0 {
|
||||||
|
topChannel := channels[0]
|
||||||
|
|
||||||
|
state.SetCurrentChannel(topChannel.ID.String())
|
||||||
|
state.SetLastChannel(target, topChannel.ID.String())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.SetCurrentChannel(last)
|
||||||
|
}
|
||||||
|
|
||||||
|
ListChannelsCommand()
|
||||||
|
ListUsersCommand()
|
||||||
|
|
||||||
|
lib.UpdatePresence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SwitchGuildsCommand() {
|
||||||
|
lib.MakePrompt(":guild> ", false, func(input string, interrupt bool) {
|
||||||
|
fmt.Print("\r")
|
||||||
|
SwitchGuild(input)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func SwitchChannelsCommand() {
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
|
||||||
|
if currentGuild == "" {
|
||||||
|
fmt.Print("<not in a guild>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lib.MakePrompt(":channel> ", false, func(input string, interrupt bool) {
|
||||||
|
fmt.Print("\r")
|
||||||
|
if input == "" {
|
||||||
|
ListUsersCommand()
|
||||||
|
} else {
|
||||||
|
target := ""
|
||||||
|
|
||||||
|
channels := GetSortedChannels(currentGuild, false, false)
|
||||||
|
|
||||||
|
for _, channel := range channels {
|
||||||
|
if strings.Index(strings.ToLower(channel.Name), strings.ToLower(input)) > -1 {
|
||||||
|
target = channel.ID.String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target == "" {
|
||||||
|
fmt.Print("<channel not found>\n\r")
|
||||||
|
} else {
|
||||||
|
state.SetCurrentChannel(target)
|
||||||
|
state.SetLastChannel(currentGuild, target)
|
||||||
|
|
||||||
|
ListUsersCommand()
|
||||||
|
|
||||||
|
lib.UpdatePresence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
83
commands/help.go
Normal file
83
commands/help.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/mgutz/ansi"
|
||||||
|
)
|
||||||
|
|
||||||
|
const format string = " %s - %s%s"
|
||||||
|
|
||||||
|
func lessLower(sa, sb string) bool {
|
||||||
|
for {
|
||||||
|
rb, nb := utf8.DecodeRuneInString(sb)
|
||||||
|
if nb == 0 {
|
||||||
|
// The number of runes in sa is greater than or
|
||||||
|
// equal to the number of runes in sb. It follows
|
||||||
|
// that sa is not less than sb.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ra, na := utf8.DecodeRuneInString(sa)
|
||||||
|
if na == 0 {
|
||||||
|
// The number of runes in sa is less than the
|
||||||
|
// number of runes in sb. It follows that sa
|
||||||
|
// is less than sb.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
rbl := unicode.ToLower(rb)
|
||||||
|
ral := unicode.ToLower(ra)
|
||||||
|
|
||||||
|
if ral != rbl {
|
||||||
|
return ral < rbl
|
||||||
|
} else {
|
||||||
|
return ra > rb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HelpCommand() {
|
||||||
|
noColor := state.HasNoColor()
|
||||||
|
|
||||||
|
fmt.Println("\r\nCOMcord (c)left 2023\n\r")
|
||||||
|
|
||||||
|
commands := GetAllCommands()
|
||||||
|
keys := make([]string, 0, len(commands))
|
||||||
|
for key := range commands {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return lessLower(keys[i], keys[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
index := 0
|
||||||
|
for _, key := range keys {
|
||||||
|
cmd := commands[key]
|
||||||
|
str := fmt.Sprintf(format, key, cmd.Description, "")
|
||||||
|
length := len(str)
|
||||||
|
padding := strings.Repeat(" ", 25 - length)
|
||||||
|
|
||||||
|
if noColor {
|
||||||
|
fmt.Printf(format, key, cmd.Description, padding)
|
||||||
|
} else {
|
||||||
|
coloredKey := ansi.Color(key, "yellow+b")
|
||||||
|
fmt.Printf(format, coloredKey, cmd.Description, padding)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
if index % 3 == 0 {
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index % 3 != 0 {
|
||||||
|
fmt.Print("\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\r\nTo begin TALK MODE, press [SPACE]\n\r")
|
||||||
|
}
|
157
commands/history.go
Normal file
157
commands/history.go
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/lib"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetHistory(limit int, channel string) {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
parsedChannelId, err := discord.ParseSnowflake(channel)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
messages, err := client.Messages(discord.ChannelID(parsedChannelId), 100)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get messages: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, j := 0, len(messages) - 1; i < j; i, j = i + 1, j - 1 {
|
||||||
|
messages[i], messages[j] = messages[j], messages[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
state.SetInPrompt(true)
|
||||||
|
|
||||||
|
fmt.Print("--Beginning-Review", strings.Repeat("-", 62), "\n\r")
|
||||||
|
|
||||||
|
lines := make([]string, 0)
|
||||||
|
for _, msg := range messages {
|
||||||
|
msgLines := lib.ProcessMessage(msg, lib.MessageOptions{NoColor: true, InHistory: true})
|
||||||
|
for _, line := range msgLines {
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
length := len(lines)
|
||||||
|
startIndex := length - limit
|
||||||
|
if startIndex < 0 {
|
||||||
|
startIndex = 0
|
||||||
|
}
|
||||||
|
for i := startIndex; i < length; i++ {
|
||||||
|
fmt.Print(lines[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("--Review-Complete", strings.Repeat("-", 63), "\n\r")
|
||||||
|
|
||||||
|
state.SetInPrompt(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HistoryCommand() {
|
||||||
|
currentChannel := state.GetCurrentChannel()
|
||||||
|
if currentChannel == "" {
|
||||||
|
fmt.Print("<not in a channel>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
GetHistory(20, currentChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtendedHistoryCommand() {
|
||||||
|
currentChannel := state.GetCurrentChannel()
|
||||||
|
if currentChannel == "" {
|
||||||
|
fmt.Print("<not in a channel>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lib.MakePrompt(":lines> ", false, func(input string, interrupt bool) {
|
||||||
|
fmt.Print("\r")
|
||||||
|
limit, err := strconv.Atoi(input)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<not a number>\n\r")
|
||||||
|
} else {
|
||||||
|
GetHistory(limit, currentChannel)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func PeekHistory(guild, channel string) {
|
||||||
|
target := ""
|
||||||
|
|
||||||
|
channels := GetSortedChannels(guild, false, false)
|
||||||
|
|
||||||
|
for _, c := range channels {
|
||||||
|
if strings.Index(strings.ToLower(c.Name), strings.ToLower(channel)) > -1 {
|
||||||
|
target = c.ID.String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target == "" {
|
||||||
|
fmt.Print("<channel not found>\n\r")
|
||||||
|
} else {
|
||||||
|
GetHistory(20, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PeekCommand() {
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
if currentGuild == "" {
|
||||||
|
fmt.Print("<not in a guild>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lib.MakePrompt(":peek> ", false, func(input string, interrupt bool) {
|
||||||
|
fmt.Print("\r")
|
||||||
|
|
||||||
|
if input != "" {
|
||||||
|
PeekHistory(currentGuild, input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CrossPeekCommand() {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
lib.MakePrompt(":guild> ", false, func(input string, interrupt bool) {
|
||||||
|
fmt.Print("\r")
|
||||||
|
|
||||||
|
if input != "" {
|
||||||
|
targetGuild := ""
|
||||||
|
|
||||||
|
guilds, err := client.GuildStore.Guilds()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get guilds: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, guild := range guilds {
|
||||||
|
if strings.Index(strings.ToLower(guild.Name), strings.ToLower(input)) > -1 {
|
||||||
|
targetGuild = guild.ID.String()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetGuild == "" {
|
||||||
|
fmt.Print("<guild not found>\n\r")
|
||||||
|
} else {
|
||||||
|
lib.MakePrompt(":peek> ", false, func(input string, interrupt bool) {
|
||||||
|
fmt.Print("\r")
|
||||||
|
|
||||||
|
if input != "" {
|
||||||
|
PeekHistory(targetGuild, input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
91
commands/main.go
Normal file
91
commands/main.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
var commandMap map[string]Command
|
||||||
|
|
||||||
|
type Command struct {
|
||||||
|
Run func()
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Setup() {
|
||||||
|
commandMap = make(map[string]Command)
|
||||||
|
|
||||||
|
commandMap["q"] = Command{
|
||||||
|
Run: QuitCommand,
|
||||||
|
Description: "quit comcord",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["h"] = Command{
|
||||||
|
Run: HelpCommand,
|
||||||
|
Description: "command help",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["c"] = Command{
|
||||||
|
Run: ClearCommand,
|
||||||
|
Description: "clear",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["e"] = Command{
|
||||||
|
Run: EmoteCommand,
|
||||||
|
Description: "emote",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["L"] = Command{
|
||||||
|
Run: ListGuildsCommand,
|
||||||
|
Description: "list guilds",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["l"] = Command{
|
||||||
|
Run: ListChannelsCommand,
|
||||||
|
Description: "list channels",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["G"] = Command{
|
||||||
|
Run: SwitchGuildsCommand,
|
||||||
|
Description: "goto guild",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["g"] = Command{
|
||||||
|
Run: SwitchChannelsCommand,
|
||||||
|
Description: "goto channel",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["w"] = Command{
|
||||||
|
Run: ListUsersCommand,
|
||||||
|
Description: "who is in channel",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["r"] = Command{
|
||||||
|
Run: HistoryCommand,
|
||||||
|
Description: "channel history",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["R"] = Command{
|
||||||
|
Run: ExtendedHistoryCommand,
|
||||||
|
Description: "extended history",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["p"] = Command{
|
||||||
|
Run: PeekCommand,
|
||||||
|
Description: "peek at channel",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["P"] = Command{
|
||||||
|
Run: CrossPeekCommand,
|
||||||
|
Description: "cross-guild peek",
|
||||||
|
}
|
||||||
|
|
||||||
|
commandMap["+"] = Command{
|
||||||
|
Run: TimeCommand,
|
||||||
|
Description: "current time",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommand(key string) (Command, bool) {
|
||||||
|
command, has := commandMap[key]
|
||||||
|
return command, has
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllCommands() map[string]Command {
|
||||||
|
return commandMap
|
||||||
|
}
|
16
commands/quit.go
Normal file
16
commands/quit.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func QuitCommand() {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
fmt.Print("Unlinking TTY...\n\r")
|
||||||
|
client.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
129
commands/send.go
Normal file
129
commands/send.go
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/lib"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
"github.com/mgutz/ansi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var REGEX_MENTION = regexp.MustCompile("@([a-z0-9._]{1,32})")
|
||||||
|
|
||||||
|
func SendMode() {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
|
||||||
|
channelId := state.GetCurrentChannel()
|
||||||
|
if channelId == "" {
|
||||||
|
fmt.Print("<not in a channel>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parsedChannelId, err := discord.ParseSnowflake(channelId)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, err := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<error getting channel: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guild, err := client.GuildStore.Guild(channel.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get current guild: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get self: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selfMember, err := client.MemberStore.Member(guild.ID, self.ID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to get self as member: ", err.Error(), ">\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cannotSend := false
|
||||||
|
|
||||||
|
if channel.ParentID.IsValid() {
|
||||||
|
category, err := client.ChannelStore.Channel(channel.ParentID)
|
||||||
|
if err == nil {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *category, *selfMember)
|
||||||
|
cannotSend = !perms.Has(discord.PermissionSendMessages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cannotSend {
|
||||||
|
perms := lib.ChannelPermissionsOf(*guild, *channel, *selfMember)
|
||||||
|
cannotSend = !perms.Has(discord.PermissionSendMessages)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cannotSend {
|
||||||
|
fmt.Print("<you do not have permission to send messages here>\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
length := utf8.RuneCountInString(self.Username) + 2
|
||||||
|
curLength := state.GetNameLength()
|
||||||
|
|
||||||
|
prompt := fmt.Sprintf("[%s]%s", self.Username, strings.Repeat(" ", (curLength - length) + 1))
|
||||||
|
if !state.HasNoColor() {
|
||||||
|
prompt = ansi.Color(prompt, "cyan+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Typing(channel.ID)
|
||||||
|
|
||||||
|
lib.MakePrompt(prompt, true, func(input string, interrupt bool) {
|
||||||
|
if input == "" {
|
||||||
|
if interrupt {
|
||||||
|
fmt.Print("^C<no message sent>\n\r")
|
||||||
|
} else {
|
||||||
|
fmt.Print(prompt, "<no message sent>\n\r")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Print(prompt, input, "\n\r")
|
||||||
|
|
||||||
|
input := REGEX_MENTION.ReplaceAllStringFunc(input, func(match string) string {
|
||||||
|
matches := REGEX_MENTION.FindStringSubmatch(match)
|
||||||
|
username := matches[1]
|
||||||
|
|
||||||
|
parsedGuildId, err := discord.ParseSnowflake(currentGuild)
|
||||||
|
if err != nil {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
members, err := client.MemberStore.Members(discord.GuildID(parsedGuildId))
|
||||||
|
if err != nil {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, member := range members {
|
||||||
|
if member.User.Username == username {
|
||||||
|
return member.User.ID.Mention()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := client.SendMessage(channel.ID, input)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("<failed to send message: ", err, ">\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: update afk state
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
12
commands/time.go
Normal file
12
commands/time.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TimeCommand() {
|
||||||
|
now := time.Now().UTC()
|
||||||
|
|
||||||
|
fmt.Printf("%s\n\r", now.Format("[Mon 02-Jan-06 15:04:05]"))
|
||||||
|
}
|
42
events/clock.go
Normal file
42
events/clock.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sentTime bool = false
|
||||||
|
|
||||||
|
func SetupClock() {
|
||||||
|
clock := time.NewTicker(500 * time.Millisecond)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <- clock.C: {
|
||||||
|
now := time.Now().UTC()
|
||||||
|
if now.Minute() % 15 == 0 && now.Second() < 2 && !sentTime {
|
||||||
|
if state.IsInPrompt() {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n\r", now.Format("[Mon 02-Jan-06 15:04:05]"))
|
||||||
|
}
|
||||||
|
|
||||||
|
client := state.GetClient()
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.SetNameLength(utf8.RuneCountInString(self.Username) + 2)
|
||||||
|
sentTime = true
|
||||||
|
} else if now.Second() > 2 && sentTime {
|
||||||
|
sentTime = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
13
events/main.go
Normal file
13
events/main.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/diamondburned/ningen/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Setup(session *ningen.State) {
|
||||||
|
session.PreHandler.AddHandler(Ready)
|
||||||
|
session.PreHandler.AddHandler(MessageCreate)
|
||||||
|
session.PreHandler.AddHandler(MessageUpdate)
|
||||||
|
session.PreHandler.AddHandler(ReactionAdd)
|
||||||
|
SetupClock()
|
||||||
|
}
|
88
events/messages.go
Normal file
88
events/messages.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/lib"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/gateway"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MessageCreate(msg *gateway.MessageCreateEvent) {
|
||||||
|
client := state.GetClient()
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Author.ID == self.ID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, err := client.ChannelStore.Channel(msg.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
|
||||||
|
|
||||||
|
if state.IsInPrompt() {
|
||||||
|
state.AddMessageToQueue(msg.Message)
|
||||||
|
} else {
|
||||||
|
lines := lib.ProcessMessage(msg.Message, lib.MessageOptions{NoColor: state.HasNoColor()})
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Print(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDM {
|
||||||
|
state.SetLastDM(msg.ChannelID.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MessageUpdate(msg *gateway.MessageUpdateEvent) {
|
||||||
|
client := state.GetClient()
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Author.ID == self.ID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*old, err := client.MessageStore.Message(msg.ChannelID, msg.ID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Content == old.Content {
|
||||||
|
return
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// dont process embed updates as messages
|
||||||
|
if !msg.EditedTimestamp.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, err := client.ChannelStore.Channel(msg.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
|
||||||
|
|
||||||
|
if state.IsInPrompt() {
|
||||||
|
state.AddMessageToQueue(msg.Message)
|
||||||
|
} else {
|
||||||
|
lines := lib.ProcessMessage(msg.Message, lib.MessageOptions{NoColor: state.HasNoColor()})
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Print(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDM {
|
||||||
|
state.SetLastDM(msg.ChannelID.String())
|
||||||
|
}
|
||||||
|
}
|
56
events/reactions.go
Normal file
56
events/reactions.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/lib"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
"github.com/diamondburned/arikawa/v3/gateway"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReactionAdd(event *gateway.MessageReactionAddEvent) {
|
||||||
|
client := state.GetClient()
|
||||||
|
currentChannel := state.GetCurrentChannel()
|
||||||
|
|
||||||
|
if event.ChannelID.String() != currentChannel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emote := event.Emoji.Name
|
||||||
|
if event.Emoji.IsCustom() {
|
||||||
|
emote = ":" + emote + ":"
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
nowSnowflake := discord.NewSnowflake(now)
|
||||||
|
|
||||||
|
message, err := client.MessageStore.Message(event.ChannelID, event.MessageID)
|
||||||
|
if err != nil {
|
||||||
|
message, err = client.Message(event.ChannelID, event.MessageID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := discord.Message{
|
||||||
|
Content: fmt.Sprintf("*reacted with %s*", emote),
|
||||||
|
Author: event.Member.User,
|
||||||
|
ChannelID: event.ChannelID,
|
||||||
|
GuildID: event.GuildID,
|
||||||
|
ID: discord.MessageID(nowSnowflake),
|
||||||
|
ReferencedMessage: message,
|
||||||
|
Type: discord.InlinedReplyMessage,
|
||||||
|
Timestamp: discord.Timestamp(now),
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.IsInPrompt() {
|
||||||
|
state.AddMessageToQueue(msg)
|
||||||
|
} else {
|
||||||
|
lines := lib.ProcessMessage(msg, lib.MessageOptions{NoColor: state.HasNoColor()})
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Print(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
events/ready.go
Normal file
52
events/ready.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/commands"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
"github.com/diamondburned/arikawa/v3/gateway"
|
||||||
|
"github.com/mgutz/ansi"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Ready(event *gateway.ReadyEvent) {
|
||||||
|
client := state.GetClient()
|
||||||
|
self, err := client.Me()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("\r% Failed to get self: ", err.Error(), "\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\rLogged in as: %s\n\r", ansi.Color(fmt.Sprintf("%s (%s)", self.Username, self.ID), "yellow"))
|
||||||
|
|
||||||
|
state.SetNameLength(utf8.RuneCountInString(self.Username) + 2)
|
||||||
|
|
||||||
|
commands.ListGuildsCommand()
|
||||||
|
|
||||||
|
defaultGuild := state.GetConfigValue("defaultGuild")
|
||||||
|
defaultChannel := state.GetConfigValue("defaultChannel")
|
||||||
|
if defaultGuild != "" {
|
||||||
|
parsedGuildId, err := discord.ParseSnowflake(defaultGuild)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("\r% Failed to parse guild ID: ", err.Error(), "\n\r")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guild, err := client.Guild(discord.GuildID(parsedGuildId))
|
||||||
|
if err == nil {
|
||||||
|
if defaultChannel != "" {
|
||||||
|
state.SetCurrentChannel(defaultChannel)
|
||||||
|
state.SetLastChannel(defaultGuild, defaultChannel)
|
||||||
|
}
|
||||||
|
commands.SwitchGuild(guild.Name)
|
||||||
|
} else {
|
||||||
|
fmt.Println("\r% This account is not in the defined default guild.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if defaultChannel != "" {
|
||||||
|
fmt.Println("\r% Default channel defined without defining default guild.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
go.mod
Normal file
27
go.mod
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module github.com/Cynosphere/comcord
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require (
|
||||||
|
atomicgo.dev/keyboard v0.2.9 // indirect
|
||||||
|
github.com/containerd/console v1.0.3 // indirect
|
||||||
|
github.com/diamondburned/arikawa/v3 v3.3.1 // indirect
|
||||||
|
github.com/diamondburned/ningen/v3 v3.0.0 // indirect
|
||||||
|
github.com/ergochat/readline v0.0.5 // indirect
|
||||||
|
github.com/gorilla/schema v1.2.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
|
github.com/kopoli/go-terminal-size v0.0.0-20170219200355-5c97524c8b54 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
github.com/mergestat/timediff v0.0.3 // indirect
|
||||||
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
github.com/twmb/murmur3 v1.1.3 // indirect
|
||||||
|
golang.org/x/crypto v0.1.0 // indirect
|
||||||
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
|
golang.org/x/term v0.10.0 // indirect
|
||||||
|
golang.org/x/text v0.9.0 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||||
|
)
|
350
go.sum
Normal file
350
go.sum
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
|
||||||
|
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
|
||||||
|
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
|
||||||
|
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
|
||||||
|
github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k=
|
||||||
|
github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI=
|
||||||
|
github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c=
|
||||||
|
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
|
||||||
|
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
||||||
|
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
|
||||||
|
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||||
|
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||||
|
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/diamondburned/arikawa v1.3.2 h1:ftWgP95IJGXNvCvtO5x0QBYsnFSnIBY0SvDdGoC3ILA=
|
||||||
|
github.com/diamondburned/arikawa v1.3.2/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
|
||||||
|
github.com/diamondburned/arikawa/v3 v3.1.1-0.20221103093025-87c479a2dcd4/go.mod h1:5jBSNnp82Z/EhsKa6Wk9FsOqSxfVkNZDTDBPOj47LpY=
|
||||||
|
github.com/diamondburned/arikawa/v3 v3.3.1 h1:puqs7mog383RJnUfRSiug5K+z/pC0Cbuq4yGJMQrQWg=
|
||||||
|
github.com/diamondburned/arikawa/v3 v3.3.1/go.mod h1:+ifmDonP/JdBiUOzZmVReEjPTHDUSkyqqRRmjSf9NE8=
|
||||||
|
github.com/diamondburned/ningen v1.0.0 h1:fr+7oDWA0Db73CuVeLY8SScWdW6ft/aWwkULXD0flKw=
|
||||||
|
github.com/diamondburned/ningen v1.0.0/go.mod h1:TcvJV0bK4bp7t+7m29/Tz9dCqgA0sJBKM/Igt0WkvT4=
|
||||||
|
github.com/diamondburned/ningen/v3 v3.0.0 h1:S7DF+AwOt/zuFsBMAu00mtE8MfuYqaTtDii6iJPX758=
|
||||||
|
github.com/diamondburned/ningen/v3 v3.0.0/go.mod h1:wMe9WZQiFgkH5Slr5xK8XBBqMJxWTfRDZU82wPney4Y=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/ergochat/readline v0.0.5 h1:PlmCLW9HUTnVfhFburg65pQUDKb0LB43G8hS+ygEkp8=
|
||||||
|
github.com/ergochat/readline v0.0.5/go.mod h1:8RNv74chpO0eTm6rdD1H6WZGihL5rJ+RfSlhv4fIfjg=
|
||||||
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||||
|
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||||
|
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
|
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||||
|
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
|
github.com/kopoli/go-terminal-size v0.0.0-20170219200355-5c97524c8b54 h1:0SMHxjkLKNawqUjjnMlCtEdj6uWZjv0+qDZ3F6GOADI=
|
||||||
|
github.com/kopoli/go-terminal-size v0.0.0-20170219200355-5c97524c8b54/go.mod h1:bm7MVZZvHQBfqHG5X59jrRE/3ak6HvK+/Zb6aZhLR2s=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/mergestat/timediff v0.0.3 h1:ucCNh4/ZrTPjFZ081PccNbhx9spymCJkFxSzgVuPU+Y=
|
||||||
|
github.com/mergestat/timediff v0.0.3/go.mod h1:yvMUaRu2oetc+9IbPLYBJviz6sA7xz8OXMDfhBl7YSI=
|
||||||
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
|
github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
|
||||||
|
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
|
||||||
|
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
|
||||||
|
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
|
||||||
|
github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU=
|
||||||
|
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
|
||||||
|
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
|
||||||
|
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||||
|
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/twmb/murmur3 v1.1.3 h1:D83U0XYKcHRYwYIpBKf3Pks91Z0Byda/9SJ8B6EMRcA=
|
||||||
|
github.com/twmb/murmur3 v1.1.3/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||||
|
github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448 h1:AzpBtmgdXa3uznrb3esNeEoaLqtNEwckRmaUH0qWD6w=
|
||||||
|
github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM=
|
||||||
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||||
|
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.2/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||||
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211001092434-39dca1131b70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||||
|
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
||||||
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
547
lib/messages.go
Normal file
547
lib/messages.go
Normal file
|
@ -0,0 +1,547 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
"github.com/mergestat/timediff"
|
||||||
|
"github.com/mgutz/ansi"
|
||||||
|
)
|
||||||
|
|
||||||
|
var REGEX_CODEBLOCK = regexp.MustCompile(`(?i)\x60\x60\x60(?:([a-z0-9_+\-\.]+?)\n)?\n*([^\n](?:.|\n)*?)\n*\x60\x60\x60`)
|
||||||
|
var REGEX_MENTION = regexp.MustCompile(`<@!?(\d+)>`)
|
||||||
|
var REGEX_ROLE_MENTION = regexp.MustCompile(`<@&(\d+)>`)
|
||||||
|
var REGEX_CHANNEL = regexp.MustCompile(`<#(\d+)>`)
|
||||||
|
var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`)
|
||||||
|
var REGEX_COMMAND = regexp.MustCompile(`</([^\s]+?):(\d+)>`)
|
||||||
|
var REGEX_BLOCKQUOTE = regexp.MustCompile(`^ *>>?>? +`)
|
||||||
|
var REGEX_GREENTEXT = regexp.MustCompile(`^(>.+?)(?:\n|$)`)
|
||||||
|
var REGEX_SPOILER = regexp.MustCompile(`\|\|(.+?)\|\|`)
|
||||||
|
var REGEX_BOLD = regexp.MustCompile(`\*\*(.+?)\*\*`)
|
||||||
|
var REGEX_UNDERLINE = regexp.MustCompile(`__(.+?)__`)
|
||||||
|
var REGEX_ITALIC_1 = regexp.MustCompile(`\*(.+?)\*`)
|
||||||
|
var REGEX_ITALIC_2 = regexp.MustCompile(`_(.+?)_`)
|
||||||
|
var REGEX_STRIKE = regexp.MustCompile(`~~(.+?)~~`)
|
||||||
|
var REGEX_3Y3 = regexp.MustCompile(`[\x{e0020}-\x{e007e}]{1,}`)
|
||||||
|
var REGEX_TIMESTAMP = regexp.MustCompile(`<t:(-?\d{1,17})(?::(t|T|d|D|f|F|R))?>`)
|
||||||
|
|
||||||
|
type MessageOptions struct {
|
||||||
|
Content string
|
||||||
|
Name string
|
||||||
|
Channel discord.ChannelID
|
||||||
|
Bot bool
|
||||||
|
Webhook bool
|
||||||
|
Attachments []discord.Attachment
|
||||||
|
Stickers []discord.StickerItem
|
||||||
|
Reply *discord.Message
|
||||||
|
Timestamp time.Time
|
||||||
|
IsMention bool
|
||||||
|
IsDM bool
|
||||||
|
IsJoin bool
|
||||||
|
IsPin bool
|
||||||
|
IsDump bool
|
||||||
|
NoColor bool
|
||||||
|
InHistory bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse3y3(content string) string {
|
||||||
|
out := []rune{}
|
||||||
|
for i, w := 0, 0; i < len(content); i += w {
|
||||||
|
runeValue, width := utf8.DecodeRuneInString(content[i:])
|
||||||
|
w = width
|
||||||
|
|
||||||
|
out = append(out, rune(int(runeValue) - 0xe0000))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReplaceStyledMarkdown(content string) string {
|
||||||
|
content = REGEX_BLOCKQUOTE.ReplaceAllString(content, ansi.Color("\u258e", "black+h"))
|
||||||
|
content = REGEX_GREENTEXT.ReplaceAllStringFunc(content, func(match string) string {
|
||||||
|
return ansi.Color(match, "green")
|
||||||
|
})
|
||||||
|
|
||||||
|
if state.GetConfigValue("enable3y3") == "true" {
|
||||||
|
parsed := REGEX_3Y3.FindString(content)
|
||||||
|
parsed = Parse3y3(parsed)
|
||||||
|
parsed = "\033[3m" + parsed + "\033[23m"
|
||||||
|
content = REGEX_3Y3.ReplaceAllString(content, ansi.Color(parsed, "magenta"))
|
||||||
|
}
|
||||||
|
|
||||||
|
content = REGEX_SPOILER.ReplaceAllString(content, "\033[30m\033[40m$1\033[39m\033[49m")
|
||||||
|
content = REGEX_STRIKE.ReplaceAllString(content, "\033[9m$1\033[29m")
|
||||||
|
content = REGEX_BOLD.ReplaceAllString(content, "\033[1m$1\033[22m")
|
||||||
|
content = REGEX_UNDERLINE.ReplaceAllString(content, "\033[4m$1\033[24m")
|
||||||
|
content = REGEX_ITALIC_1.ReplaceAllString(content, "\033[3m$1\033[23m")
|
||||||
|
content = REGEX_ITALIC_2.ReplaceAllString(content, "\033[3m$1\033[23m")
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceAllWithCallback(re regexp.Regexp, content string, callback func(matches []string) string) string {
|
||||||
|
return re.ReplaceAllStringFunc(content, func(match string) string {
|
||||||
|
matches := re.FindStringSubmatch(match)
|
||||||
|
return callback(matches)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReplaceMarkdown(content string, noColor bool) string {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
content = replaceAllWithCallback(*REGEX_MENTION, content, func(matches []string) string {
|
||||||
|
id := matches[1]
|
||||||
|
parsedId, err := discord.ParseSnowflake(id)
|
||||||
|
if err != nil {
|
||||||
|
return "@Unknown User"
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
if currentGuild == "" {
|
||||||
|
user, err := client.User(discord.UserID(parsedId))
|
||||||
|
if err != nil {
|
||||||
|
return "@Unknown User"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "@" + user.Username
|
||||||
|
} else {
|
||||||
|
parsedGuildId, err := discord.ParseSnowflake(currentGuild)
|
||||||
|
if err != nil {
|
||||||
|
return "@Unknown User"
|
||||||
|
}
|
||||||
|
|
||||||
|
member, err := client.MemberStore.Member(discord.GuildID(parsedGuildId), discord.UserID(parsedId))
|
||||||
|
if err != nil {
|
||||||
|
return "@Unknown User"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "@" + member.User.Username
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
content = replaceAllWithCallback(*REGEX_ROLE_MENTION, content, func(matches []string) string {
|
||||||
|
id := matches[1]
|
||||||
|
parsedId, err := discord.ParseSnowflake(id)
|
||||||
|
if err != nil {
|
||||||
|
return "[@Unknown Role]"
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
if currentGuild == "" {
|
||||||
|
return "[@Unknown Role]"
|
||||||
|
}
|
||||||
|
parsedGuildId, err := discord.ParseSnowflake(currentGuild)
|
||||||
|
if err != nil {
|
||||||
|
return "[@Unknown Role]"
|
||||||
|
}
|
||||||
|
|
||||||
|
role, err := client.RoleStore.Role(discord.GuildID(parsedGuildId), discord.RoleID(parsedId))
|
||||||
|
if err != nil {
|
||||||
|
return "[@Unknown Role]"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[@%s]", role.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
content = replaceAllWithCallback(*REGEX_CHANNEL, content, func(matches []string) string {
|
||||||
|
id := matches[1]
|
||||||
|
parsedId, err := discord.ParseSnowflake(id)
|
||||||
|
if err != nil {
|
||||||
|
return "#Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, err := client.ChannelStore.Channel(discord.ChannelID(parsedId))
|
||||||
|
if err != nil {
|
||||||
|
return "#Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "#" + channel.Name
|
||||||
|
})
|
||||||
|
|
||||||
|
content = REGEX_EMOTE.ReplaceAllString(content, ":$1:")
|
||||||
|
content = REGEX_COMMAND.ReplaceAllString(content, "/$1")
|
||||||
|
|
||||||
|
content = replaceAllWithCallback(*REGEX_TIMESTAMP, content, func (matches []string) string {
|
||||||
|
timestamp, err := strconv.Atoi(matches[1])
|
||||||
|
if err != nil {
|
||||||
|
return "Invalid Date"
|
||||||
|
}
|
||||||
|
timeObj := time.Unix(int64(timestamp), 0).UTC()
|
||||||
|
|
||||||
|
format := matches[2]
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case "t":
|
||||||
|
return timeObj.Format("15:04")
|
||||||
|
case "T":
|
||||||
|
return timeObj.Format("15:04:05")
|
||||||
|
case "d":
|
||||||
|
return timeObj.Format("2006/01/02")
|
||||||
|
case "D":
|
||||||
|
return timeObj.Format("2 January 2006")
|
||||||
|
case "f":
|
||||||
|
default:
|
||||||
|
return timeObj.Format("2 January 2006 15:04")
|
||||||
|
case "F":
|
||||||
|
return timeObj.Format("Monday, 2 January 2006 15:04")
|
||||||
|
case "R":
|
||||||
|
return timediff.TimeDiff(timeObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Invalid Date"
|
||||||
|
})
|
||||||
|
|
||||||
|
if !noColor {
|
||||||
|
content = ReplaceStyledMarkdown(content)
|
||||||
|
} else {
|
||||||
|
if state.GetConfigValue("enable3y3") == "true" {
|
||||||
|
parsed := REGEX_3Y3.FindString(content)
|
||||||
|
parsed = Parse3y3(parsed)
|
||||||
|
content = REGEX_3Y3.ReplaceAllString(content, "<3y3:" + parsed + ">")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatMessage(options MessageOptions) []string {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
lines := make([]string, 0)
|
||||||
|
|
||||||
|
timestamp := options.Timestamp.UTC().Format("[15:04:05]")
|
||||||
|
|
||||||
|
nameLength := utf8.RuneCountInString(options.Name) + 2
|
||||||
|
stateNameLength := state.GetNameLength()
|
||||||
|
if nameLength > stateNameLength {
|
||||||
|
state.SetNameLength(nameLength)
|
||||||
|
stateNameLength = nameLength
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Reply != nil {
|
||||||
|
nameColor := "cyan+b"
|
||||||
|
if options.Reply.Author.Bot {
|
||||||
|
nameColor = "yellow+b"
|
||||||
|
}
|
||||||
|
|
||||||
|
headerLength := 6 + utf8.RuneCountInString(options.Reply.Author.Username)
|
||||||
|
|
||||||
|
content := options.Reply.Content
|
||||||
|
replyContent := strings.ReplaceAll(content, "\n", " ")
|
||||||
|
|
||||||
|
replyContent = ReplaceMarkdown(replyContent, options.NoColor)
|
||||||
|
|
||||||
|
attachmentCount := len(options.Reply.Attachments)
|
||||||
|
if attachmentCount > 0 {
|
||||||
|
attachmentPlural := ""
|
||||||
|
if attachmentCount > 1 {
|
||||||
|
attachmentPlural = "s"
|
||||||
|
}
|
||||||
|
|
||||||
|
replyContent = strings.TrimSpace(replyContent + fmt.Sprintf(" <%d attachment%s>", attachmentCount, attachmentPlural))
|
||||||
|
}
|
||||||
|
|
||||||
|
stickerCount := len(options.Reply.Stickers)
|
||||||
|
if stickerCount > 0 {
|
||||||
|
stickerPlural := ""
|
||||||
|
if stickerCount > 0 {
|
||||||
|
stickerPlural = "s"
|
||||||
|
}
|
||||||
|
|
||||||
|
replyContent = strings.TrimSpace(replyContent + fmt.Sprintf(" <%d sticker%s>", stickerCount, stickerPlural))
|
||||||
|
}
|
||||||
|
|
||||||
|
length := headerLength + utf8.RuneCountInString(replyContent)
|
||||||
|
|
||||||
|
replySymbol := " \u00bb "
|
||||||
|
if !options.NoColor {
|
||||||
|
replySymbol = ansi.Color(replySymbol, "white+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := fmt.Sprintf("[%s] ", options.Reply.Author.Username)
|
||||||
|
if !options.NoColor {
|
||||||
|
name = ansi.Color(name, nameColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
moreContent := "\u2026"
|
||||||
|
if !options.NoColor {
|
||||||
|
moreContent = ansi.Color(moreContent, "reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > 79 {
|
||||||
|
replyContent = replyContent[:79 - headerLength] + moreContent
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, replySymbol, name, replyContent, "\n\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.IsDump {
|
||||||
|
if options.InHistory {
|
||||||
|
headerLength := 80 - (utf8.RuneCountInString(options.Name) + 5)
|
||||||
|
dumpHeader := fmt.Sprintf("--- %s %s\n\r", options.Name, strings.Repeat("-", headerLength))
|
||||||
|
|
||||||
|
contentLines := strings.Split(options.Content, "\n")
|
||||||
|
|
||||||
|
lines = append(lines, dumpHeader)
|
||||||
|
for _, line := range contentLines {
|
||||||
|
lines = append(lines, line + "\n\r")
|
||||||
|
}
|
||||||
|
lines = append(lines, dumpHeader)
|
||||||
|
} else {
|
||||||
|
wordCount := len(strings.Split(options.Content, " "))
|
||||||
|
lineCount := len(strings.Split(options.Content, "\n"))
|
||||||
|
wordsPlural := ""
|
||||||
|
linesPlural := ""
|
||||||
|
|
||||||
|
if wordCount > 1 {
|
||||||
|
wordsPlural = "s"
|
||||||
|
}
|
||||||
|
if lineCount > 1 {
|
||||||
|
linesPlural = "s"
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("<%s DUMPs in %d characters of %d word%s in %d line%s>", options.Name, len(options.Content), wordCount, wordsPlural, lineCount, linesPlural)
|
||||||
|
|
||||||
|
if !options.NoColor {
|
||||||
|
str = ansi.Color(str, "yellow+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, str + "\n\r")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
content := options.Content
|
||||||
|
|
||||||
|
if options.IsDM {
|
||||||
|
name := fmt.Sprintf("*%s*", options.Name)
|
||||||
|
if !options.NoColor {
|
||||||
|
name = ansi.Color(name, "red+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
content = ReplaceMarkdown(content, options.NoColor)
|
||||||
|
|
||||||
|
lines = append(lines, fmt.Sprintf("%s %s\x07\n\r", name, content))
|
||||||
|
} else if utf8.RuneCountInString(content) > 1 &&
|
||||||
|
(strings.HasPrefix(content, "*") && strings.HasSuffix(content, "*") && !strings.HasPrefix(content, "**") && !strings.HasSuffix(content, "**")) ||
|
||||||
|
(strings.HasPrefix(content, "_") && strings.HasSuffix(content, "_") && !strings.HasPrefix(content, "__") && !strings.HasSuffix(content, "__")) {
|
||||||
|
str := fmt.Sprintf("<%s %s>", options.Name, content[1:len(content)-1])
|
||||||
|
|
||||||
|
if !options.NoColor {
|
||||||
|
str = ansi.Color(str, "green+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, str + "\n\r")
|
||||||
|
} else if options.IsJoin {
|
||||||
|
channel, err := client.ChannelStore.Channel(options.Channel)
|
||||||
|
if err != nil {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
guild, err := client.GuildStore.Guild(channel.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("%s %s has joined %s", timestamp, options.Name, guild.Name)
|
||||||
|
if !options.NoColor {
|
||||||
|
str = ansi.Color(str, "yellow+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, str + "\n\r")
|
||||||
|
} else if options.IsPin {
|
||||||
|
str := fmt.Sprintf("%s %s pinned a message to this channel", timestamp, options.Name)
|
||||||
|
if !options.NoColor {
|
||||||
|
str = ansi.Color(str, "yellow+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, str + "\n\r")
|
||||||
|
} else {
|
||||||
|
nameColor := "cyan+b"
|
||||||
|
if options.IsMention {
|
||||||
|
nameColor = "red+b"
|
||||||
|
} else if options.Webhook {
|
||||||
|
nameColor = "magenta+b"
|
||||||
|
} else if options.Bot {
|
||||||
|
nameColor = "yellow+b"
|
||||||
|
}
|
||||||
|
|
||||||
|
content = ReplaceMarkdown(content, options.NoColor)
|
||||||
|
|
||||||
|
name := fmt.Sprintf("[%s]", options.Name)
|
||||||
|
if !options.NoColor {
|
||||||
|
name = ansi.Color(name, nameColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
padding := strings.Repeat(" ", int(math.Abs(float64(stateNameLength) - float64(nameLength))) + 1)
|
||||||
|
str := name + padding + content
|
||||||
|
if options.IsMention {
|
||||||
|
str = str + "\x07"
|
||||||
|
}
|
||||||
|
lines = append(lines, str + "\n\r")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(options.Attachments) > 0 {
|
||||||
|
for _, attachment := range options.Attachments {
|
||||||
|
str := fmt.Sprintf("<attachment: %s >", attachment.URL)
|
||||||
|
if !options.NoColor {
|
||||||
|
str = ansi.Color(str, "yellow+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, str + "\n\r")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(options.Stickers) > 0 {
|
||||||
|
for _, sticker := range options.Stickers {
|
||||||
|
str := fmt.Sprintf("<sticker: \"%s\" https://cdn.discordapp.com/stickers/%s.png >", sticker.Name, sticker.ID)
|
||||||
|
if !options.NoColor {
|
||||||
|
str = ansi.Color(str, "yellow+b")
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, str + "\n\r")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: links
|
||||||
|
|
||||||
|
// TODO: embeds
|
||||||
|
|
||||||
|
// TODO: lines output for history
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessMessage(msg discord.Message, options MessageOptions) []string {
|
||||||
|
client := state.GetClient()
|
||||||
|
lines := make([]string, 0)
|
||||||
|
|
||||||
|
channel, err := client.ChannelStore.Channel(msg.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
guild, err := client.GuildStore.Guild(channel.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
selfMember, err := client.MemberStore.Member(guild.ID, self.ID)
|
||||||
|
if err != nil {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMentionedRole := false
|
||||||
|
for _, role := range msg.MentionRoleIDs {
|
||||||
|
for _, selfRole := range selfMember.RoleIDs {
|
||||||
|
if role == selfRole {
|
||||||
|
hasMentionedRole = true
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirectlyMentioned := false
|
||||||
|
for _, user := range msg.Mentions {
|
||||||
|
if user.ID == self.ID {
|
||||||
|
isDirectlyMentioned = true
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isPing := msg.MentionEveryone || hasMentionedRole || isDirectlyMentioned
|
||||||
|
isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
|
||||||
|
isEdit := msg.EditedTimestamp.IsValid()
|
||||||
|
|
||||||
|
currentChannel := state.GetCurrentChannel()
|
||||||
|
isCurrentChannel := currentChannel == msg.ChannelID.String()
|
||||||
|
|
||||||
|
if !isCurrentChannel && !isDM && !isPing && !options.InHistory {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
if isPing && !isCurrentChannel && !isDM && !options.InHistory {
|
||||||
|
str := fmt.Sprintf("**mentioned by %s in #%s in %s**", msg.Author.Username, channel.Name, guild.Name)
|
||||||
|
if !options.NoColor {
|
||||||
|
str = ansi.Color(str, "red+b")
|
||||||
|
}
|
||||||
|
str = str + "\x07\n\r"
|
||||||
|
lines = append(lines, str)
|
||||||
|
} else {
|
||||||
|
content := msg.Content
|
||||||
|
if isEdit {
|
||||||
|
content = content + " (edited)"
|
||||||
|
}
|
||||||
|
|
||||||
|
isDump := REGEX_CODEBLOCK.MatchString(content)
|
||||||
|
|
||||||
|
if strings.Index(content, "\n") > -1 && !isDump {
|
||||||
|
for i, line := range strings.Split(content, "\n") {
|
||||||
|
options.Content = line
|
||||||
|
options.Name = msg.Author.Username
|
||||||
|
options.Channel = msg.ChannelID
|
||||||
|
options.Bot = msg.Author.Bot
|
||||||
|
options.Webhook = msg.WebhookID.IsValid()
|
||||||
|
options.Attachments = msg.Attachments
|
||||||
|
options.Stickers = msg.Stickers
|
||||||
|
if i == 0 {
|
||||||
|
options.Reply = msg.ReferencedMessage
|
||||||
|
} else {
|
||||||
|
options.Reply = nil
|
||||||
|
}
|
||||||
|
options.Timestamp = time.Time(msg.Timestamp)
|
||||||
|
options.IsMention = isPing
|
||||||
|
options.IsDM = isDM
|
||||||
|
options.IsJoin = msg.Type == discord.GuildMemberJoinMessage
|
||||||
|
options.IsPin = msg.Type == discord.ChannelPinnedMessage
|
||||||
|
options.IsDump = false
|
||||||
|
|
||||||
|
msgLines := FormatMessage(options)
|
||||||
|
for _, line := range msgLines {
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
options.Content = content
|
||||||
|
options.Name = msg.Author.Username
|
||||||
|
options.Channel = msg.ChannelID
|
||||||
|
options.Bot = msg.Author.Bot
|
||||||
|
options.Webhook = msg.WebhookID.IsValid()
|
||||||
|
options.Attachments = msg.Attachments
|
||||||
|
options.Stickers = msg.Stickers
|
||||||
|
options.Reply = msg.ReferencedMessage
|
||||||
|
options.Timestamp = time.Time(msg.Timestamp)
|
||||||
|
options.IsMention = isPing
|
||||||
|
options.IsDM = isDM
|
||||||
|
options.IsJoin = msg.Type == discord.GuildMemberJoinMessage
|
||||||
|
options.IsPin = msg.Type == discord.ChannelPinnedMessage
|
||||||
|
options.IsDump = isDump
|
||||||
|
|
||||||
|
lines = FormatMessage(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessQueue() {
|
||||||
|
queue := state.GetMessageQueue()
|
||||||
|
|
||||||
|
for _, msg := range queue {
|
||||||
|
lines := ProcessMessage(msg, MessageOptions{NoColor: state.HasNoColor()})
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Print(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.EmptyMessageQueue()
|
||||||
|
}
|
121
lib/presence.go
Normal file
121
lib/presence.go
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
"github.com/diamondburned/arikawa/v3/gateway"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UpdatePresence() {
|
||||||
|
client := state.GetClient()
|
||||||
|
|
||||||
|
self, err := client.MeStore.Me()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
afk := state.IsAFK()
|
||||||
|
presence := gateway.UpdatePresenceCommand{
|
||||||
|
Since: 0,
|
||||||
|
Activities: make([]discord.Activity, 0),
|
||||||
|
AFK: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGuild := state.GetCurrentGuild()
|
||||||
|
currentChannel := state.GetCurrentChannel()
|
||||||
|
|
||||||
|
parsedGuildId, err := discord.ParseSnowflake(currentGuild)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parsedChannelId, err := discord.ParseSnowflake(currentChannel)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var activity discord.Activity
|
||||||
|
|
||||||
|
startTime := state.GetStartTime()
|
||||||
|
|
||||||
|
if self.Bot {
|
||||||
|
activity = discord.Activity{
|
||||||
|
Type: discord.GameActivity,
|
||||||
|
Name: "comcord",
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentGuild != "" && currentChannel != "" {
|
||||||
|
guild, guildErr := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
|
||||||
|
channel, channelErr := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
|
||||||
|
|
||||||
|
if guildErr == nil && channelErr == nil {
|
||||||
|
activity.Type = discord.CustomActivity
|
||||||
|
activity.Name = "comcord"
|
||||||
|
activity.State = fmt.Sprintf("#%s - %s | comcord", channel.Name, guild.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if afk {
|
||||||
|
activity.State = activity.State + " [AFK]"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsedAppId, err := discord.ParseSnowflake("1026163285877325874")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
activity = discord.Activity{
|
||||||
|
Type: 0,
|
||||||
|
AppID: discord.AppID(parsedAppId),
|
||||||
|
Name: "comcord",
|
||||||
|
Timestamps: &discord.ActivityTimestamps{
|
||||||
|
Start: discord.UnixMsTimestamp(startTime.Unix()),
|
||||||
|
},
|
||||||
|
/*Buttons: make([]string, 0),
|
||||||
|
Metadata: ActivityMetadata{
|
||||||
|
ButtonURLs: make([]string, 0),
|
||||||
|
},*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//activity.Buttons = append(activity.Buttons, "comcord Repo")
|
||||||
|
//activity.Metadata.ButtonURLs = append(activity.Metadata.ButtonURLs, "https://gitdab.com/Cynosphere/comcord")
|
||||||
|
|
||||||
|
if currentGuild != "" && currentChannel != "" {
|
||||||
|
guild, guildErr := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
|
||||||
|
channel, channelErr := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
|
||||||
|
|
||||||
|
if guildErr == nil && channelErr == nil {
|
||||||
|
activity.Details = fmt.Sprintf("#%s - %s", channel.Name, guild.Name)
|
||||||
|
|
||||||
|
activity.Assets = &discord.ActivityAssets{}
|
||||||
|
activity.Assets.LargeText = guild.Name
|
||||||
|
if guild.Icon != "" {
|
||||||
|
activity.Assets.LargeImage = fmt.Sprintf("mp:icons/%s/%s.png?size=1024", guild.ID, guild.Icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if afk {
|
||||||
|
activity.State = "AFK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.CreatedAt = discord.UnixTimestamp(startTime.Unix())
|
||||||
|
|
||||||
|
presence.Activities = append(presence.Activities, activity)
|
||||||
|
|
||||||
|
defaultStatus := state.GetConfigValue("defaultStatus")
|
||||||
|
if defaultStatus != "" {
|
||||||
|
presence.Status = discord.Status(defaultStatus)
|
||||||
|
} else {
|
||||||
|
if afk {
|
||||||
|
presence.Status = discord.IdleStatus
|
||||||
|
} else {
|
||||||
|
presence.Status = discord.OnlineStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Gateway().Send(context.Background(), &presence)
|
||||||
|
}
|
32
lib/prompt.go
Normal file
32
lib/prompt.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/ergochat/readline"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakePrompt(prompt string, uniqueLine bool, callback func(input string, interrupt bool)) {
|
||||||
|
state.SetInPrompt(true)
|
||||||
|
state.SetPromptText(prompt)
|
||||||
|
|
||||||
|
rl, _ := readline.NewFromConfig(&readline.Config{
|
||||||
|
Prompt: prompt,
|
||||||
|
UniqueEditLine: uniqueLine,
|
||||||
|
})
|
||||||
|
defer rl.Close()
|
||||||
|
|
||||||
|
input, err := rl.Readline()
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
rl.Close()
|
||||||
|
|
||||||
|
interrupt := err == readline.ErrInterrupt
|
||||||
|
|
||||||
|
callback(input, interrupt)
|
||||||
|
|
||||||
|
state.SetInPrompt(false)
|
||||||
|
state.SetPromptText("")
|
||||||
|
|
||||||
|
ProcessQueue()
|
||||||
|
}
|
79
lib/util.go
Normal file
79
lib/util.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import "github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
|
||||||
|
func GuildPermissionsOf(guild discord.Guild, member discord.Member) discord.Permissions {
|
||||||
|
if guild.OwnerID == member.User.ID {
|
||||||
|
return discord.PermissionAll
|
||||||
|
}
|
||||||
|
|
||||||
|
var perm discord.Permissions
|
||||||
|
|
||||||
|
for _, role := range guild.Roles {
|
||||||
|
if role.ID == discord.RoleID(guild.ID) {
|
||||||
|
perm |= role.Permissions
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perm.Has(discord.PermissionAdministrator) {
|
||||||
|
return discord.PermissionAll
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range guild.Roles {
|
||||||
|
for _, id := range member.RoleIDs {
|
||||||
|
if id == role.ID {
|
||||||
|
perm |= role.Permissions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perm.Has(discord.PermissionAdministrator) {
|
||||||
|
return discord.PermissionAll
|
||||||
|
}
|
||||||
|
|
||||||
|
return perm
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelPermissionsOf(guild discord.Guild, channel discord.Channel, member discord.Member) discord.Permissions {
|
||||||
|
perm := GuildPermissionsOf(guild, member)
|
||||||
|
|
||||||
|
if perm.Has(discord.PermissionAdministrator) {
|
||||||
|
return discord.PermissionAll
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, overwrite := range channel.Overwrites {
|
||||||
|
if discord.GuildID(overwrite.ID) == guild.ID {
|
||||||
|
perm &= ^overwrite.Deny
|
||||||
|
perm |= overwrite.Allow
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var deny, allow discord.Permissions
|
||||||
|
|
||||||
|
for _, overwrite := range channel.Overwrites {
|
||||||
|
for _, id := range member.RoleIDs {
|
||||||
|
if id == discord.RoleID(overwrite.ID) && overwrite.Type == discord.OverwriteRole {
|
||||||
|
deny |= overwrite.Deny
|
||||||
|
allow |= overwrite.Allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perm &= ^deny
|
||||||
|
perm |= allow
|
||||||
|
|
||||||
|
for _, overwrite := range channel.Overwrites {
|
||||||
|
if discord.UserID(overwrite.ID) == member.User.ID && overwrite.Type == discord.OverwriteMember {
|
||||||
|
perm &= ^overwrite.Deny
|
||||||
|
perm |= overwrite.Allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perm.Has(discord.PermissionAdministrator) {
|
||||||
|
return discord.PermissionAll
|
||||||
|
}
|
||||||
|
|
||||||
|
return perm
|
||||||
|
}
|
185
main.go
Normal file
185
main.go
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"atomicgo.dev/keyboard"
|
||||||
|
"atomicgo.dev/keyboard/keys"
|
||||||
|
"github.com/Cynosphere/comcord/commands"
|
||||||
|
"github.com/Cynosphere/comcord/events"
|
||||||
|
"github.com/Cynosphere/comcord/rcfile"
|
||||||
|
"github.com/Cynosphere/comcord/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
"github.com/diamondburned/arikawa/v3/gateway"
|
||||||
|
"github.com/diamondburned/arikawa/v3/session"
|
||||||
|
arikawa_state "github.com/diamondburned/arikawa/v3/state"
|
||||||
|
"github.com/diamondburned/arikawa/v3/state/store/defaultstore"
|
||||||
|
"github.com/diamondburned/arikawa/v3/utils/handler"
|
||||||
|
"github.com/diamondburned/ningen/v3"
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer term.Restore(int(os.Stdin.Fd()), oldState)
|
||||||
|
|
||||||
|
var config map[string]string = make(map[string]string)
|
||||||
|
var token string
|
||||||
|
|
||||||
|
homeDir, homeErr := os.UserHomeDir()
|
||||||
|
if homeErr != nil {
|
||||||
|
panic(homeErr)
|
||||||
|
}
|
||||||
|
RCPATH := rcfile.GetPath()
|
||||||
|
|
||||||
|
_, rcErr := os.Stat(RCPATH)
|
||||||
|
if !os.IsNotExist(rcErr) {
|
||||||
|
fmt.Printf("%% Reading %s ...\n", strings.Replace(RCPATH, homeDir, "~", 1))
|
||||||
|
config = rcfile.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
token = os.Args[1]
|
||||||
|
if os.IsNotExist(rcErr) {
|
||||||
|
fmt.Println("% Writing token to ~/.comcordrc")
|
||||||
|
config["token"] = token
|
||||||
|
rcfile.Save(config)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configToken, tokenInConfig := config["token"]
|
||||||
|
if tokenInConfig {
|
||||||
|
token = configToken
|
||||||
|
} else {
|
||||||
|
fmt.Println("No token provided.")
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\rCOMcord (c)left 2023")
|
||||||
|
fmt.Println("\rType 'h' for Commands")
|
||||||
|
fmt.Print("\r")
|
||||||
|
|
||||||
|
commands.Setup()
|
||||||
|
|
||||||
|
allowUserAccounts := config["allowUserAccounts"] == "true"
|
||||||
|
tokenPrefix := "Bot "
|
||||||
|
if allowUserAccounts {
|
||||||
|
tokenPrefix = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fullToken := tokenPrefix + token
|
||||||
|
|
||||||
|
props := gateway.IdentifyProperties{
|
||||||
|
OS: runtime.GOOS,
|
||||||
|
}
|
||||||
|
statusType := config["statusType"]
|
||||||
|
if statusType == "mobile" {
|
||||||
|
props.Browser = "Discord Android"
|
||||||
|
} else if statusType == "embedded" {
|
||||||
|
props.Browser = "Discord Embedded"
|
||||||
|
} else if statusType == "desktop" {
|
||||||
|
props.Browser = "Discord Client"
|
||||||
|
} else {
|
||||||
|
props.Browser = "comcord"
|
||||||
|
}
|
||||||
|
|
||||||
|
ident := gateway.IdentifyCommand{
|
||||||
|
Token: fullToken,
|
||||||
|
Properties: props,
|
||||||
|
|
||||||
|
Compress: true,
|
||||||
|
LargeThreshold: 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
status := "online"
|
||||||
|
defaultStatus := config["defaultStatus"]
|
||||||
|
if defaultStatus != "" {
|
||||||
|
status = defaultStatus
|
||||||
|
}
|
||||||
|
startTime := state.GetStartTime()
|
||||||
|
|
||||||
|
activity := discord.Activity{
|
||||||
|
Name: "comcord",
|
||||||
|
Type: discord.GameActivity,
|
||||||
|
CreatedAt: discord.UnixTimestamp(startTime.Unix()),
|
||||||
|
Timestamps: &discord.ActivityTimestamps{
|
||||||
|
Start: discord.UnixMsTimestamp(startTime.Unix()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
presence := gateway.UpdatePresenceCommand{
|
||||||
|
Since: 0,
|
||||||
|
Activities: make([]discord.Activity, 0),
|
||||||
|
Status: discord.Status(status),
|
||||||
|
AFK: false,
|
||||||
|
}
|
||||||
|
presence.Activities = append(presence.Activities, activity)
|
||||||
|
ident.Presence = &presence
|
||||||
|
|
||||||
|
gwURL, err := gateway.URL(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("% Failed to get gateway URL: ", err, "\n\r")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
gw := gateway.NewCustomWithIdentifier(gateway.AddGatewayParams(gwURL), gateway.NewIdentifier(ident), nil)
|
||||||
|
ses := session.NewWithGateway(gw, handler.New())
|
||||||
|
st := arikawa_state.NewFromSession(ses, defaultstore.New())
|
||||||
|
client := ningen.FromState(st)
|
||||||
|
client.PreHandler = handler.New()
|
||||||
|
|
||||||
|
client.AddIntents(gateway.IntentGuilds)
|
||||||
|
client.AddIntents(gateway.IntentGuildPresences)
|
||||||
|
client.AddIntents(gateway.IntentGuildMembers)
|
||||||
|
client.AddIntents(gateway.IntentGuildMessages)
|
||||||
|
client.AddIntents(gateway.IntentGuildMessageReactions)
|
||||||
|
client.AddIntents(gateway.IntentDirectMessages)
|
||||||
|
client.AddIntents(gateway.IntentDirectMessageReactions)
|
||||||
|
client.AddIntents(gateway.IntentMessageContent)
|
||||||
|
|
||||||
|
state.Setup(config, client)
|
||||||
|
events.Setup(client)
|
||||||
|
|
||||||
|
err = client.Open(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print("% Failed to connect to Discord: ", err, "\n\r")
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
keyboard.Listen(func(key keys.Key) (stop bool, err error) {
|
||||||
|
if !state.IsInPrompt() {
|
||||||
|
if key.Code == keys.CtrlC {
|
||||||
|
term.Restore(int(os.Stdin.Fd()), oldState)
|
||||||
|
commands.QuitCommand()
|
||||||
|
return true, nil
|
||||||
|
} else {
|
||||||
|
command, has := commands.GetCommand(key.String())
|
||||||
|
if has {
|
||||||
|
if key.String() == "q" {
|
||||||
|
term.Restore(int(os.Stdin.Fd()), oldState)
|
||||||
|
}
|
||||||
|
command.Run()
|
||||||
|
} else {
|
||||||
|
commands.SendMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
/*sc := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||||
|
<-sc
|
||||||
|
|
||||||
|
client.Close()*/
|
||||||
|
}
|
14
package.json
14
package.json
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"name": "comcord",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"keywords": [],
|
|
||||||
"author": "Cynosphere",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@projectdysnomia/dysnomia": "^0.1.2",
|
|
||||||
"chalk": "4.1.2",
|
|
||||||
"discord-rpc": "^4.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
191
pnpm-lock.yaml
191
pnpm-lock.yaml
|
@ -1,191 +0,0 @@
|
||||||
lockfileVersion: '6.0'
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
'@projectdysnomia/dysnomia':
|
|
||||||
specifier: ^0.1.2
|
|
||||||
version: 0.1.2
|
|
||||||
chalk:
|
|
||||||
specifier: 4.1.2
|
|
||||||
version: 4.1.2
|
|
||||||
discord-rpc:
|
|
||||||
specifier: ^4.0.1
|
|
||||||
version: 4.0.1
|
|
||||||
|
|
||||||
packages:
|
|
||||||
|
|
||||||
/@projectdysnomia/dysnomia@0.1.2:
|
|
||||||
resolution: {integrity: sha512-F64G64JwFWn/QUFqkhsyBvXJ0Du3E6Y0yu8tSrcukAnSeW8qV+reKqeQnMmHcQWYopwuYM8Q6OF/VX6VKggtOA==}
|
|
||||||
engines: {node: '>=10.4.0'}
|
|
||||||
peerDependencies:
|
|
||||||
'@discordjs/opus': ^0.9.0
|
|
||||||
erlpack: github:discord/erlpack || github:abalabahaha/erlpack
|
|
||||||
eventemitter3: ^5.0.0
|
|
||||||
pako: ^2.1.0
|
|
||||||
sodium-native: ^4.0.1
|
|
||||||
zlib-sync: ^0.1.8
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@discordjs/opus':
|
|
||||||
optional: true
|
|
||||||
erlpack:
|
|
||||||
optional: true
|
|
||||||
eventemitter3:
|
|
||||||
optional: true
|
|
||||||
pako:
|
|
||||||
optional: true
|
|
||||||
sodium-native:
|
|
||||||
optional: true
|
|
||||||
zlib-sync:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
ws: 8.13.0
|
|
||||||
optionalDependencies:
|
|
||||||
opusscript: 0.0.8
|
|
||||||
tweetnacl: 1.0.3
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- bufferutil
|
|
||||||
- utf-8-validate
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ansi-styles@4.3.0:
|
|
||||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
color-convert: 2.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/bindings@1.5.0:
|
|
||||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
|
||||||
dependencies:
|
|
||||||
file-uri-to-path: 1.0.0
|
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/chalk@4.1.2:
|
|
||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
ansi-styles: 4.3.0
|
|
||||||
supports-color: 7.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/color-convert@2.0.1:
|
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
|
||||||
engines: {node: '>=7.0.0'}
|
|
||||||
dependencies:
|
|
||||||
color-name: 1.1.4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/color-name@1.1.4:
|
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/discord-rpc@4.0.1:
|
|
||||||
resolution: {integrity: sha512-HOvHpbq5STRZJjQIBzwoKnQ0jHplbEWFWlPDwXXKm/bILh4nzjcg7mNqll0UY7RsjFoaXA7e/oYb/4lvpda2zA==}
|
|
||||||
dependencies:
|
|
||||||
node-fetch: 2.6.7
|
|
||||||
ws: 7.5.9
|
|
||||||
optionalDependencies:
|
|
||||||
register-scheme: github.com/devsnek/node-register-scheme/e7cc9a63a1f512565da44cb57316d9fb10750e17
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- bufferutil
|
|
||||||
- encoding
|
|
||||||
- utf-8-validate
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/file-uri-to-path@1.0.0:
|
|
||||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/has-flag@4.0.0:
|
|
||||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/node-addon-api@1.7.2:
|
|
||||||
resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==}
|
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/node-fetch@2.6.7:
|
|
||||||
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
|
|
||||||
engines: {node: 4.x || >=6.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
encoding: ^0.1.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
encoding:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
whatwg-url: 5.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/opusscript@0.0.8:
|
|
||||||
resolution: {integrity: sha512-VSTi1aWFuCkRCVq+tx/BQ5q9fMnQ9pVZ3JU4UHKqTkf0ED3fKEPdr+gKAAl3IA2hj9rrP6iyq3hlcJq3HELtNQ==}
|
|
||||||
requiresBuild: true
|
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/supports-color@7.2.0:
|
|
||||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
dependencies:
|
|
||||||
has-flag: 4.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tr46@0.0.3:
|
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/tweetnacl@1.0.3:
|
|
||||||
resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==}
|
|
||||||
requiresBuild: true
|
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/webidl-conversions@3.0.1:
|
|
||||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/whatwg-url@5.0.0:
|
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
|
||||||
dependencies:
|
|
||||||
tr46: 0.0.3
|
|
||||||
webidl-conversions: 3.0.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ws@7.5.9:
|
|
||||||
resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==}
|
|
||||||
engines: {node: '>=8.3.0'}
|
|
||||||
peerDependencies:
|
|
||||||
bufferutil: ^4.0.1
|
|
||||||
utf-8-validate: ^5.0.2
|
|
||||||
peerDependenciesMeta:
|
|
||||||
bufferutil:
|
|
||||||
optional: true
|
|
||||||
utf-8-validate:
|
|
||||||
optional: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ws@8.13.0:
|
|
||||||
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
|
|
||||||
engines: {node: '>=10.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
bufferutil: ^4.0.1
|
|
||||||
utf-8-validate: '>=5.0.2'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
bufferutil:
|
|
||||||
optional: true
|
|
||||||
utf-8-validate:
|
|
||||||
optional: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
github.com/devsnek/node-register-scheme/e7cc9a63a1f512565da44cb57316d9fb10750e17:
|
|
||||||
resolution: {tarball: https://codeload.github.com/devsnek/node-register-scheme/tar.gz/e7cc9a63a1f512565da44cb57316d9fb10750e17}
|
|
||||||
name: register-scheme
|
|
||||||
version: 0.0.2
|
|
||||||
requiresBuild: true
|
|
||||||
dependencies:
|
|
||||||
bindings: 1.5.0
|
|
||||||
node-addon-api: 1.7.2
|
|
||||||
dev: false
|
|
||||||
optional: true
|
|
47
rcfile/main.go
Normal file
47
rcfile/main.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package rcfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPath() string {
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(homeDir, ".comcordrc")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load() map[string]string {
|
||||||
|
config := make(map[string]string)
|
||||||
|
file, err := os.ReadFile(GetPath())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(file), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
kvs := strings.Split(line, "=")
|
||||||
|
if len(kvs) == 2 {
|
||||||
|
config[kvs[0]] = kvs[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func Save(config map[string]string) {
|
||||||
|
out := ""
|
||||||
|
|
||||||
|
for key, value := range config {
|
||||||
|
out = out + key + "=" + value + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
err := os.WriteFile(GetPath(), []byte(out), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
const {updatePresence} = require("../lib/presence");
|
|
||||||
|
|
||||||
addCommand("A", "toggles AFK mode", function () {
|
|
||||||
if (comcord.state.afk == true) {
|
|
||||||
comcord.state.afk = false;
|
|
||||||
comcord.client.editStatus("online");
|
|
||||||
comcord.client.editAFK(false);
|
|
||||||
console.log("<you have returned>");
|
|
||||||
} else {
|
|
||||||
comcord.state.afk = true;
|
|
||||||
comcord.client.editStatus("idle");
|
|
||||||
comcord.client.editAFK(true);
|
|
||||||
console.log("<you go AFK>");
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePresence();
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
|
|
||||||
addCommand("c", "clear", function () {
|
|
||||||
console.clear();
|
|
||||||
});
|
|
|
@ -1,25 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
const {startPrompt} = require("../lib/prompt");
|
|
||||||
|
|
||||||
addCommand("e", "emote", function () {
|
|
||||||
if (!comcord.state.currentChannel) {
|
|
||||||
console.log("<not in a channel>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
startPrompt(":emote> ", async function (input) {
|
|
||||||
if (input == "") {
|
|
||||||
console.log("<no message sent>");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
process.stdout.write("\n");
|
|
||||||
await comcord.client.createMessage(comcord.state.currentChannel, {
|
|
||||||
content: `*${input}*`,
|
|
||||||
});
|
|
||||||
console.log(`<${comcord.client.user.username} ${input}>`);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(`<failed to send message: ${err.message}>`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,29 +0,0 @@
|
||||||
const chalk = require("chalk");
|
|
||||||
|
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
|
|
||||||
addCommand("h", "command help", function () {
|
|
||||||
console.log("\nCOMcord (c)left 2022\n");
|
|
||||||
|
|
||||||
const keys = Object.keys(comcord.commands);
|
|
||||||
keys.sort((a, b) => a.localeCompare(b));
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
for (const key of keys) {
|
|
||||||
const desc = comcord.commands[key].name;
|
|
||||||
const length = ` ${key} - ${desc}`.length;
|
|
||||||
|
|
||||||
process.stdout.write(
|
|
||||||
" " +
|
|
||||||
chalk.bold.yellow(key) +
|
|
||||||
chalk.reset(" - " + desc) +
|
|
||||||
" ".repeat(Math.abs(25 - length))
|
|
||||||
);
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (index % 3 == 0) process.stdout.write("\n");
|
|
||||||
}
|
|
||||||
if (index % 3 != 0) process.stdout.write("\n");
|
|
||||||
|
|
||||||
console.log("\nTo begin TALK MODE, press [SPACE]\n");
|
|
||||||
});
|
|
|
@ -1,126 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
const {startPrompt} = require("../lib/prompt");
|
|
||||||
const {processMessage} = require("../lib/messages");
|
|
||||||
const {listChannels} = require("./listChannels");
|
|
||||||
|
|
||||||
async function getHistory(limit = 20, channel = null) {
|
|
||||||
if (!channel && !comcord.state.currentChannel) {
|
|
||||||
console.log("<not in a channel>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = await comcord.client.getMessages(
|
|
||||||
channel ?? comcord.state.currentChannel
|
|
||||||
);
|
|
||||||
messages.reverse();
|
|
||||||
|
|
||||||
console.log("--Beginning-Review".padEnd(72, "-"));
|
|
||||||
|
|
||||||
const lines = [];
|
|
||||||
for (const msg of messages) {
|
|
||||||
const processedLines = processMessage(msg, {noColor: true, history: true});
|
|
||||||
if (processedLines) lines.push(...processedLines);
|
|
||||||
}
|
|
||||||
console.log(lines.slice(-limit).join("\n"));
|
|
||||||
|
|
||||||
console.log("--Review-Complete".padEnd(73, "-"));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getExtendedHistory(input) {
|
|
||||||
input = parseInt(input);
|
|
||||||
if (isNaN(input)) {
|
|
||||||
console.log("<not a number>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await getHistory(input);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(`<failed to get history: ${err.message}>`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommand("r", "channel history", getHistory);
|
|
||||||
addCommand("R", "extended history", function () {
|
|
||||||
startPrompt(":lines> ", async function (input) {
|
|
||||||
process.stdout.write("\n");
|
|
||||||
await getExtendedHistory(input);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
addCommand("p", "peek at channel", function () {
|
|
||||||
if (!comcord.state.currentGuild) {
|
|
||||||
console.log("<not in a guild>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
startPrompt(":peek> ", async function (input) {
|
|
||||||
console.log("");
|
|
||||||
if (input == "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let target;
|
|
||||||
|
|
||||||
const guild = comcord.client.guilds.get(comcord.state.currentGuild);
|
|
||||||
const channels = [...guild.channels.values()].filter((c) => c.type == 0);
|
|
||||||
channels.sort((a, b) => a.position - b.position);
|
|
||||||
|
|
||||||
for (const channel of channels) {
|
|
||||||
if (channel.name.toLowerCase().indexOf(input.toLowerCase()) > -1) {
|
|
||||||
target = channel.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target == null) {
|
|
||||||
console.log("<channel not found>");
|
|
||||||
} else {
|
|
||||||
await getHistory(20, target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
addCommand("P", "cross-guild peek", function () {
|
|
||||||
startPrompt(":guild> ", async function (input) {
|
|
||||||
console.log("");
|
|
||||||
if (input == "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let targetGuild;
|
|
||||||
for (const guild of comcord.client.guilds.values()) {
|
|
||||||
if (guild.name.toLowerCase().indexOf(input.toLowerCase()) > -1) {
|
|
||||||
targetGuild = guild.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetGuild == null) {
|
|
||||||
console.log("<guild not found>");
|
|
||||||
} else {
|
|
||||||
startPrompt(":peek> ", async function (input) {
|
|
||||||
console.log("");
|
|
||||||
if (input == "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let target;
|
|
||||||
|
|
||||||
const guild = comcord.client.guilds.get(targetGuild);
|
|
||||||
const channels = [...guild.channels.values()].filter(
|
|
||||||
(c) => c.type == 0
|
|
||||||
);
|
|
||||||
channels.sort((a, b) => a.position - b.position);
|
|
||||||
|
|
||||||
for (const channel of channels) {
|
|
||||||
if (channel.name.toLowerCase().indexOf(input.toLowerCase()) > -1) {
|
|
||||||
target = channel.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target == null) {
|
|
||||||
console.log("<channel not found>");
|
|
||||||
} else {
|
|
||||||
await getHistory(20, target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,55 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
|
|
||||||
function listChannels() {
|
|
||||||
if (!comcord.state.currentGuild) {
|
|
||||||
console.log("<not in a guild>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let longest = 0;
|
|
||||||
const guild = comcord.client.guilds.get(comcord.state.currentGuild);
|
|
||||||
const channels = Array.from(guild.channels.values()).filter(
|
|
||||||
(c) => c.type == 0 || c.type == 5
|
|
||||||
);
|
|
||||||
channels.sort((a, b) => a.position - b.position);
|
|
||||||
|
|
||||||
for (const channel of channels) {
|
|
||||||
const perms = channel.permissionsOf(comcord.client.user.id);
|
|
||||||
const private = !perms.has("readMessageHistory");
|
|
||||||
|
|
||||||
if (channel.name.length + (private ? 1 : 0) > longest)
|
|
||||||
longest = Math.min(25, channel.name.length + (private ? 1 : 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("");
|
|
||||||
console.log(" " + "channel-name".padStart(longest, " ") + " " + "topic");
|
|
||||||
console.log("-".repeat(80));
|
|
||||||
for (const channel of channels) {
|
|
||||||
const topic =
|
|
||||||
channel.topic != null ? channel.topic.replace(/\n/g, " ") : "";
|
|
||||||
const perms = channel.permissionsOf(comcord.client.user.id);
|
|
||||||
const private = !perms.has("viewChannel");
|
|
||||||
|
|
||||||
const name = (private ? "*" : "") + channel.name;
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
" " +
|
|
||||||
(name.length > 24 ? name.substring(0, 24) + "\u2026" : name).padStart(
|
|
||||||
longest,
|
|
||||||
" "
|
|
||||||
) +
|
|
||||||
" " +
|
|
||||||
(topic.length > 80 - (longest + 5)
|
|
||||||
? topic.substring(0, 79 - (longest + 5)) + "\u2026"
|
|
||||||
: topic)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
console.log("-".repeat(80));
|
|
||||||
console.log("");
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommand("l", "list channels", listChannels);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
listChannels,
|
|
||||||
};
|
|
|
@ -1,37 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
|
|
||||||
function listGuilds() {
|
|
||||||
let longest = 0;
|
|
||||||
const guilds = [];
|
|
||||||
|
|
||||||
for (const guild of comcord.client.guilds.values()) {
|
|
||||||
if (guild.name.length > longest) longest = guild.name.length;
|
|
||||||
|
|
||||||
const online = Array.from(guild.members.values()).filter(
|
|
||||||
(m) => m.status
|
|
||||||
).length;
|
|
||||||
guilds.push({name: guild.name, members: guild.memberCount, online});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("");
|
|
||||||
console.log(" " + "guild-name".padStart(longest, " ") + " online total");
|
|
||||||
console.log("-".repeat(80));
|
|
||||||
for (const guild of guilds) {
|
|
||||||
console.log(
|
|
||||||
" " +
|
|
||||||
guild.name.padStart(longest, " ") +
|
|
||||||
" " +
|
|
||||||
guild.online.toString().padStart(6, " ") +
|
|
||||||
" " +
|
|
||||||
guild.members.toString().padStart(5, " ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
console.log("-".repeat(80));
|
|
||||||
console.log("");
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommand("L", "list guilds", listGuilds);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
listGuilds,
|
|
||||||
};
|
|
|
@ -1,88 +0,0 @@
|
||||||
const chalk = require("chalk");
|
|
||||||
|
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
|
|
||||||
function getStatus(status) {
|
|
||||||
let color;
|
|
||||||
switch (status) {
|
|
||||||
case "online":
|
|
||||||
color = chalk.bold.green;
|
|
||||||
break;
|
|
||||||
case "idle":
|
|
||||||
color = chalk.bold.yellow;
|
|
||||||
break;
|
|
||||||
case "dnd":
|
|
||||||
color = chalk.bold.red;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = chalk.bold;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return color(" \u2022 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function listUsers() {
|
|
||||||
if (!comcord.state.currentGuild) {
|
|
||||||
console.log("<not in a guild>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!comcord.state.currentChannel) {
|
|
||||||
console.log("<not in a channel>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const guild = comcord.client.guilds.get(comcord.state.currentGuild);
|
|
||||||
const channel = guild.channels.get(comcord.state.currentChannel);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`\n[you are in '${guild.name}' in '${channel.name}' among ${guild.memberCount}]\n`
|
|
||||||
);
|
|
||||||
|
|
||||||
const online = Array.from(guild.members.values()).filter((m) => m.status);
|
|
||||||
online.sort((a, b) => a.username.localeCompare(b.username));
|
|
||||||
|
|
||||||
let longest = 0;
|
|
||||||
for (const member of online) {
|
|
||||||
// FIXME: remove discrim stuff after username migration finished
|
|
||||||
const name = member.username;
|
|
||||||
if (name.length + 3 > longest) longest = name.length + 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = Math.floor(process.stdout.columns / longest);
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
for (const member of online) {
|
|
||||||
const name = member.username;
|
|
||||||
const status = getStatus(member.status);
|
|
||||||
const nameAndStatus =
|
|
||||||
(member.user.bot ? chalk.yellow(name) : chalk.reset(name)) + status;
|
|
||||||
|
|
||||||
index++;
|
|
||||||
process.stdout.write(
|
|
||||||
nameAndStatus +
|
|
||||||
" ".repeat(
|
|
||||||
index % columns == 0 ? 0 : Math.abs(longest - (name.length + 3))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (index % columns == 0) process.stdout.write("\n");
|
|
||||||
}
|
|
||||||
if (index % columns != 0) process.stdout.write("\n");
|
|
||||||
console.log("");
|
|
||||||
|
|
||||||
if (channel.topic != null) {
|
|
||||||
console.log("--Topic".padEnd(80, "-"));
|
|
||||||
console.log(channel.topic);
|
|
||||||
console.log("-".repeat(80));
|
|
||||||
console.log("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!comcord.commands.w) {
|
|
||||||
addCommand("w", "who is in guild", listUsers);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
listUsers,
|
|
||||||
};
|
|
|
@ -1,60 +0,0 @@
|
||||||
const chalk = require("chalk");
|
|
||||||
|
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
const {startPrompt} = require("../lib/prompt");
|
|
||||||
const {listUsers} = require("./listUsers");
|
|
||||||
|
|
||||||
function startDM(channel) {
|
|
||||||
startPrompt(":msg> ", async function (input) {
|
|
||||||
if (input == "") {
|
|
||||||
console.log(
|
|
||||||
`\n<message not sent to ${channel.recipent?.username ?? "group DM"}>`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
await channel.createMessage({content: input});
|
|
||||||
console.log(
|
|
||||||
chalk.bold.green(
|
|
||||||
`\n<message sent to ${channel.recipient?.username ?? "group DM"}>`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(`\n<failed to send message: ${err.message}>`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommand("s", "send private", function () {
|
|
||||||
console.log("Provide a RECIPIENT");
|
|
||||||
startPrompt(":to> ", function (who) {
|
|
||||||
let target;
|
|
||||||
for (const user of comcord.client.users.values()) {
|
|
||||||
if (user.username == who) {
|
|
||||||
target = user;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target) {
|
|
||||||
console.log("");
|
|
||||||
startDM(target);
|
|
||||||
} else {
|
|
||||||
listUsers();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
addCommand("a", "answer a send", function () {
|
|
||||||
if (comcord.state.lastDM) {
|
|
||||||
console.log(
|
|
||||||
chalk.bold.green(
|
|
||||||
`<answering ${comcord.state.lastDM.recipient?.username ?? "group DM"}>`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
startDM(comcord.state.lastDM);
|
|
||||||
} else {
|
|
||||||
// FIXME: figure out the actual message in com
|
|
||||||
console.log("<no one to answer>");
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
|
|
||||||
addCommand("q", "quit comcord", function () {
|
|
||||||
comcord.state.quitting = true;
|
|
||||||
comcord.client.disconnect({reconnect: false});
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
|
@ -1,44 +0,0 @@
|
||||||
const chalk = require("chalk");
|
|
||||||
|
|
||||||
const {startPrompt} = require("../lib/prompt");
|
|
||||||
const {updatePresence} = require("../lib/presence");
|
|
||||||
|
|
||||||
function sendMode() {
|
|
||||||
if (!comcord.state.currentChannel) {
|
|
||||||
console.log("<not in a channel>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
startPrompt(
|
|
||||||
chalk.bold.cyan(`[${comcord.client.user.username}]`) +
|
|
||||||
" ".repeat(
|
|
||||||
comcord.state.nameLength - (comcord.client.user.username.length + 2)
|
|
||||||
) +
|
|
||||||
chalk.reset(" "),
|
|
||||||
async function (input) {
|
|
||||||
if (input == "") {
|
|
||||||
console.log("<no message sent>");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
process.stdout.write("\n");
|
|
||||||
await comcord.client.createMessage(comcord.state.currentChannel, {
|
|
||||||
content: input,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (comcord.state.afk == true) {
|
|
||||||
comcord.state.afk = false;
|
|
||||||
comcord.client.editStatus("online");
|
|
||||||
comcord.client.editAFK(false);
|
|
||||||
console.log("<you have returned>");
|
|
||||||
|
|
||||||
updatePresence();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log("<failed to send message: " + err.message + ">");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {sendMode};
|
|
|
@ -1,47 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
const {startPrompt} = require("../lib/prompt");
|
|
||||||
const {updatePresence} = require("../lib/presence");
|
|
||||||
|
|
||||||
const {listUsers} = require("./listUsers");
|
|
||||||
|
|
||||||
function switchChannel(input) {
|
|
||||||
if (input == "") {
|
|
||||||
listUsers();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let target;
|
|
||||||
|
|
||||||
const guild = comcord.client.guilds.get(comcord.state.currentGuild);
|
|
||||||
const channels = [...guild.channels.values()].filter((c) => c.type == 0);
|
|
||||||
channels.sort((a, b) => a.position - b.position);
|
|
||||||
|
|
||||||
for (const channel of channels) {
|
|
||||||
if (channel.name.toLowerCase().indexOf(input.toLowerCase()) > -1) {
|
|
||||||
target = channel.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target == null) {
|
|
||||||
console.log("<channel not found>");
|
|
||||||
} else {
|
|
||||||
comcord.state.currentChannel = target;
|
|
||||||
comcord.state.lastChannel.set(comcord.state.currentGuild, target);
|
|
||||||
|
|
||||||
listUsers();
|
|
||||||
|
|
||||||
const channel = guild.channels.get(comcord.state.currentChannel);
|
|
||||||
|
|
||||||
process.title = `${guild.name} - ${channel.name} - comcord`;
|
|
||||||
|
|
||||||
updatePresence();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommand("g", "goto channel", function () {
|
|
||||||
if (!comcord.state.currentGuild) {
|
|
||||||
console.log("<not in a guild>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
startPrompt(":channel> ", switchChannel);
|
|
||||||
});
|
|
|
@ -1,60 +0,0 @@
|
||||||
const {addCommand} = require("../lib/command");
|
|
||||||
const {startPrompt} = require("../lib/prompt");
|
|
||||||
const {updatePresence} = require("../lib/presence");
|
|
||||||
|
|
||||||
const {listChannels} = require("./listChannels");
|
|
||||||
const {listUsers} = require("./listUsers");
|
|
||||||
|
|
||||||
function findTopChannel(guildId) {
|
|
||||||
const guild = comcord.client.guilds.get(guildId);
|
|
||||||
const channels = [...guild.channels.values()].filter((c) => c.type == 0);
|
|
||||||
channels.sort((a, b) => a.position - b.position);
|
|
||||||
|
|
||||||
return channels[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function switchGuild(input) {
|
|
||||||
if (input == "") {
|
|
||||||
listChannels();
|
|
||||||
listUsers();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let target;
|
|
||||||
|
|
||||||
for (const guild of comcord.client.guilds.values()) {
|
|
||||||
if (guild.name.toLowerCase().indexOf(input.toLowerCase()) > -1) {
|
|
||||||
target = guild.id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target == null) {
|
|
||||||
console.log("<guild not found>");
|
|
||||||
} else {
|
|
||||||
comcord.state.currentGuild = target;
|
|
||||||
if (!comcord.state.lastChannel.has(target)) {
|
|
||||||
const topChannel = findTopChannel(target);
|
|
||||||
comcord.state.currentChannel = topChannel.id;
|
|
||||||
comcord.state.lastChannel.set(target, topChannel.id);
|
|
||||||
} else {
|
|
||||||
comcord.state.currentChannel = comcord.state.lastChannel.get(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
listChannels();
|
|
||||||
listUsers();
|
|
||||||
|
|
||||||
const guild = comcord.client.guilds.get(comcord.state.currentGuild);
|
|
||||||
const channel = guild.channels.get(comcord.state.currentChannel);
|
|
||||||
|
|
||||||
process.title = `${guild.name} - ${channel.name} - comcord`;
|
|
||||||
|
|
||||||
updatePresence();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommand("G", "goto guild", function () {
|
|
||||||
startPrompt(":guild> ", switchGuild);
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = {switchGuild};
|
|
470
src/index.js
470
src/index.js
|
@ -1,470 +0,0 @@
|
||||||
const {Client, Constants, Channel} = require("@projectdysnomia/dysnomia");
|
|
||||||
const DiscordRPC = require("discord-rpc");
|
|
||||||
const chalk = require("chalk");
|
|
||||||
const fs = require("fs");
|
|
||||||
const os = require("os");
|
|
||||||
|
|
||||||
const rcfile = require("./lib/rcfile");
|
|
||||||
const config = {};
|
|
||||||
|
|
||||||
if (fs.existsSync(rcfile.path)) {
|
|
||||||
console.log(`% Reading ${rcfile.path.replace(os.homedir(), "~")} ...`);
|
|
||||||
rcfile.readFile(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
const CLIENT_ID = "1026163285877325874";
|
|
||||||
|
|
||||||
const token = process.argv[2];
|
|
||||||
if (!config.token && token) {
|
|
||||||
console.log("% Writing token to .comcordrc");
|
|
||||||
config.token = token;
|
|
||||||
rcfile.writeFile(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.token && !token) {
|
|
||||||
console.log("No token provided.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.title = "comcord";
|
|
||||||
|
|
||||||
global.comcord = {
|
|
||||||
config,
|
|
||||||
state: {
|
|
||||||
connected: true,
|
|
||||||
rpcConnected: false,
|
|
||||||
startTime: Date.now(),
|
|
||||||
currentGuild: null,
|
|
||||||
currentChannel: null,
|
|
||||||
nameLength: 2,
|
|
||||||
inPrompt: false,
|
|
||||||
messageQueue: [],
|
|
||||||
lastChannel: new Map(),
|
|
||||||
afk: false,
|
|
||||||
},
|
|
||||||
commands: {},
|
|
||||||
};
|
|
||||||
const client = new Client(
|
|
||||||
(config.allowUserAccounts == "true" ? "" : "Bot ") + (token ?? config.token),
|
|
||||||
{
|
|
||||||
defaultImageFormat: "png",
|
|
||||||
defaultImageSize: 1024,
|
|
||||||
gateway: {
|
|
||||||
intents: Object.values(Constants.Intents),
|
|
||||||
},
|
|
||||||
allowedMentions: {
|
|
||||||
everyone: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
comcord.client = client;
|
|
||||||
const rpc = new DiscordRPC.Client({transport: "ipc"});
|
|
||||||
comcord.rpc = rpc;
|
|
||||||
|
|
||||||
const {finalizePrompt} = require("./lib/prompt");
|
|
||||||
const {processMessage, processQueue} = require("./lib/messages");
|
|
||||||
const {updatePresence} = require("./lib/presence");
|
|
||||||
|
|
||||||
require("./commands/quit");
|
|
||||||
require("./commands/clear");
|
|
||||||
require("./commands/help");
|
|
||||||
const {sendMode} = require("./commands/send");
|
|
||||||
require("./commands/emote");
|
|
||||||
const {listGuilds} = require("./commands/listGuilds");
|
|
||||||
const {switchGuild} = require("./commands/switchGuild"); // loads listChannels and listUsers
|
|
||||||
require("./commands/switchChannel"); //loads listUsers
|
|
||||||
require("./commands/history"); // includes extended history
|
|
||||||
require("./commands/afk");
|
|
||||||
require("./commands/privateMessages");
|
|
||||||
|
|
||||||
process.stdin.setRawMode(true);
|
|
||||||
process.stdin.resume();
|
|
||||||
process.stdin.setEncoding("utf8");
|
|
||||||
|
|
||||||
client.once("ready", function () {
|
|
||||||
console.log(
|
|
||||||
"Logged in as: " +
|
|
||||||
chalk.yellow(`${client.user?.username} (${client.user?.id})`)
|
|
||||||
);
|
|
||||||
comcord.state.nameLength = (client.user?.username?.length ?? 0) + 2;
|
|
||||||
|
|
||||||
listGuilds();
|
|
||||||
|
|
||||||
if (config.defaultGuild) {
|
|
||||||
const guild = client.guilds.get(config.defaultGuild);
|
|
||||||
if (guild != null) {
|
|
||||||
if (config.defaultChannel) {
|
|
||||||
comcord.state.currentChannel = config.defaultChannel;
|
|
||||||
comcord.state.lastChannel.set(
|
|
||||||
config.defaultGuild,
|
|
||||||
config.defaultChannel
|
|
||||||
);
|
|
||||||
}
|
|
||||||
switchGuild(guild.name);
|
|
||||||
} else {
|
|
||||||
console.log("% This account is not in the defined default guild.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (config.defaultChannel) {
|
|
||||||
console.log("% Default channel defined without defining default guild.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client.user.bot && !config.disableRPC) {
|
|
||||||
rpc
|
|
||||||
.login({
|
|
||||||
clientId: CLIENT_ID,
|
|
||||||
})
|
|
||||||
.catch(function () {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
client.on("error", function () {});
|
|
||||||
client.on("ready", function () {
|
|
||||||
if (comcord.state.connected === false) {
|
|
||||||
console.log("% Reconnected");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
client.on("disconnect", function () {
|
|
||||||
if (!comcord.state.quitting) {
|
|
||||||
comcord.state.connected = false;
|
|
||||||
console.log("% Disconnected, retrying...");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rpc.on("connected", function () {
|
|
||||||
comcord.state.rpcConnected = true;
|
|
||||||
updatePresence();
|
|
||||||
});
|
|
||||||
let retryingRPC = false;
|
|
||||||
rpc.once("ready", function () {
|
|
||||||
rpc.transport.on("error", function () {});
|
|
||||||
rpc.transport.on("close", function () {
|
|
||||||
comcord.state.rpcConnected = false;
|
|
||||||
if (!retryingRPC) {
|
|
||||||
retryingRPC = true;
|
|
||||||
setTimeout(function () {
|
|
||||||
rpc.transport
|
|
||||||
.connect()
|
|
||||||
.then(() => {
|
|
||||||
retryingRPC = false;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
retryingRPC = false;
|
|
||||||
rpc.transport.emit("close");
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
rpc.on("error", function () {});
|
|
||||||
|
|
||||||
client.on("messageCreate", async function (msg) {
|
|
||||||
if (
|
|
||||||
(msg.mentions.find((user) => user.id == client.user.id) ||
|
|
||||||
msg.mentionEveryone) &&
|
|
||||||
msg.channel.id != comcord.state.currentChannel &&
|
|
||||||
msg.channel.type !== Constants.ChannelTypes.DM &&
|
|
||||||
msg.channel.type !== Constants.ChannelTypes.GROUP_DM
|
|
||||||
) {
|
|
||||||
const data = {ping: true, channel: msg.channel, author: msg.author};
|
|
||||||
if (comcord.state.inPrompt) {
|
|
||||||
comcord.state.messageQueue.push(data);
|
|
||||||
} else {
|
|
||||||
processMessage(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!msg.author) return;
|
|
||||||
if (msg.author.id === client.user.id) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(msg.channel instanceof Channel) &&
|
|
||||||
msg.author.id != client.user.id &&
|
|
||||||
!msg.guildID
|
|
||||||
) {
|
|
||||||
if (msg.channel.type === Constants.ChannelTypes.DM) {
|
|
||||||
const newChannel = await client.getDMChannel(msg.author.id);
|
|
||||||
if (msg.channel.id == newChannel.id) msg.channel = newChannel;
|
|
||||||
} else if (msg.channel.type === Constants.ChannelTypes.GROUP_DM) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(msg.channel instanceof Channel)) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.channel.id == comcord.state.currentChannel ||
|
|
||||||
msg.channel.type === Constants.ChannelTypes.DM ||
|
|
||||||
msg.channel.type === Constants.ChannelTypes.GROUP_DM
|
|
||||||
) {
|
|
||||||
if (comcord.state.inPrompt) {
|
|
||||||
comcord.state.messageQueue.push(msg);
|
|
||||||
} else {
|
|
||||||
processMessage(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.channel.type === Constants.ChannelTypes.DM ||
|
|
||||||
msg.channel.type === Constants.ChannelTypes.GROUP_DM
|
|
||||||
) {
|
|
||||||
comcord.state.lastDM = msg.channel;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
client.on("messageUpdate", async function (msg, old) {
|
|
||||||
if (!msg.author) return;
|
|
||||||
if (msg.author.id === client.user.id) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(msg.channel instanceof Channel) &&
|
|
||||||
msg.author.id != client.user.id &&
|
|
||||||
!msg.guildID
|
|
||||||
) {
|
|
||||||
if (msg.channel.type === Constants.ChannelTypes.DM) {
|
|
||||||
const newChannel = await client.getDMChannel(msg.author.id);
|
|
||||||
if (msg.channel.id == newChannel.id) msg.channel = newChannel;
|
|
||||||
} else if (msg.channel.type === Constants.ChannelTypes.GROUP_DM) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(msg.channel instanceof Channel)) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.channel.id == comcord.state.currentChannel ||
|
|
||||||
msg.channel.type === Constants.ChannelTypes.DM ||
|
|
||||||
msg.channel.type === Constants.ChannelTypes.GROUP_DM
|
|
||||||
) {
|
|
||||||
if (old && msg.content == old.content) return;
|
|
||||||
|
|
||||||
if (comcord.state.inPrompt) {
|
|
||||||
comcord.state.messageQueue.push(msg);
|
|
||||||
} else {
|
|
||||||
processMessage(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
msg.channel.type === Constants.ChannelTypes.DM ||
|
|
||||||
msg.channel.type === Constants.ChannelTypes.GROUP_DM
|
|
||||||
) {
|
|
||||||
comcord.state.lastDM = msg.channel;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
client.on("messageReactionAdd", async function (msg, emoji, reactor) {
|
|
||||||
if (msg.channel.id != comcord.state.currentChannel) return;
|
|
||||||
const reply =
|
|
||||||
msg.channel.messages.get(msg.id) ??
|
|
||||||
(await msg.channel
|
|
||||||
.getMessages({
|
|
||||||
limit: 1,
|
|
||||||
around: msg.id,
|
|
||||||
})
|
|
||||||
.then((msgs) => msgs[0]));
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
channel: msg.channel,
|
|
||||||
referencedMessage: reply,
|
|
||||||
author: reactor?.user ?? client.users.get(reactor.id),
|
|
||||||
timestamp: Date.now(),
|
|
||||||
mentions: [],
|
|
||||||
content: `*reacted with ${emoji.id ? `:${emoji.name}:` : emoji.name}*`,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (comcord.state.inPrompt) {
|
|
||||||
comcord.state.messageQueue.push(data);
|
|
||||||
} else {
|
|
||||||
processMessage(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
process.stdin.on("data", async function (key) {
|
|
||||||
if (comcord.state.inPrompt) {
|
|
||||||
if (key === "\r") {
|
|
||||||
await finalizePrompt();
|
|
||||||
processQueue();
|
|
||||||
} else {
|
|
||||||
if (key === "\b" || key === "\u007f") {
|
|
||||||
if (comcord.state.promptInput.length > 0) {
|
|
||||||
process.stdout.moveCursor(-1);
|
|
||||||
process.stdout.write(" ");
|
|
||||||
process.stdout.moveCursor(-1);
|
|
||||||
comcord.state.promptInput = comcord.state.promptInput.substring(
|
|
||||||
0,
|
|
||||||
comcord.state.promptInput.length - 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
key = key.replace("\u001b", "");
|
|
||||||
process.stdout.write(key);
|
|
||||||
comcord.state.promptInput += key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (comcord.commands[key]) {
|
|
||||||
comcord.commands[key].callback();
|
|
||||||
} else {
|
|
||||||
sendMode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
config.allowUserAccounts == "true" &&
|
|
||||||
!(token ?? config.token).startsWith("Bot ")
|
|
||||||
) {
|
|
||||||
if (fetch == null) {
|
|
||||||
console.log("Node v18+ needed for user account support.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
(async function () {
|
|
||||||
comcord.clientSpoof = require("./lib/clientSpoof");
|
|
||||||
const superProperties = await comcord.clientSpoof.getSuperProperties();
|
|
||||||
comcord.clientSpoof.superProperties = superProperties;
|
|
||||||
comcord.clientSpoof.superPropertiesBase64 = Buffer.from(
|
|
||||||
JSON.stringify(superProperties)
|
|
||||||
).toString("base64");
|
|
||||||
|
|
||||||
// FIXME: is there a way we can string patch functions without having to
|
|
||||||
// dump locals into global
|
|
||||||
global.MultipartData = require("@projectdysnomia/dysnomia/lib/util/MultipartData.js");
|
|
||||||
global.SequentialBucket = require("@projectdysnomia/dysnomia/lib/util/SequentialBucket.js");
|
|
||||||
global.DiscordHTTPError = require("@projectdysnomia/dysnomia/lib/errors/DiscordHTTPError.js");
|
|
||||||
global.DiscordRESTError = require("@projectdysnomia/dysnomia/lib/errors/DiscordRESTError.js");
|
|
||||||
global.Zlib = require("node:zlib");
|
|
||||||
global.HTTPS = require("node:https");
|
|
||||||
global.HTTP = require("node:http");
|
|
||||||
global.GatewayOPCodes = Constants.GatewayOPCodes;
|
|
||||||
global.GATEWAY_VERSION = Constants.GATEWAY_VERSION;
|
|
||||||
|
|
||||||
client.getGateway = async function getGateway() {
|
|
||||||
return {url: "wss://gateway.discord.gg"};
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("% Injecting headers into request handler");
|
|
||||||
client.requestHandler.userAgent = superProperties.browser_user_agent;
|
|
||||||
const requestFunction = client.requestHandler.request.toString();
|
|
||||||
const newRequest = requestFunction
|
|
||||||
.replace(
|
|
||||||
"this.userAgent,",
|
|
||||||
'this.userAgent,\n"X-Super-Properties":comcord.clientSpoof.superPropertiesBase64,'
|
|
||||||
)
|
|
||||||
.replace("._token", '._token.replace("Bot ","")');
|
|
||||||
if (requestFunction === newRequest)
|
|
||||||
throw new Error("Failed to patch request");
|
|
||||||
client.requestHandler.request = new Function(
|
|
||||||
"method",
|
|
||||||
"url",
|
|
||||||
"auth",
|
|
||||||
"body",
|
|
||||||
"file",
|
|
||||||
"_route",
|
|
||||||
"short",
|
|
||||||
`return (function ${newRequest}).apply(this,arguments)`
|
|
||||||
).bind(client.requestHandler);
|
|
||||||
|
|
||||||
console.log("% Injecting shard spawning");
|
|
||||||
client.shards._spawn = client.shards.spawn.bind(client.shards);
|
|
||||||
client.shards.spawn = function (id) {
|
|
||||||
const res = this._spawn.apply(this, [id]);
|
|
||||||
const shard = this.get(id);
|
|
||||||
if (shard) {
|
|
||||||
const identifyFunction = shard.identify.toString();
|
|
||||||
const newIdentify = identifyFunction
|
|
||||||
.replace(
|
|
||||||
/properties: {\n\s+.+?\n\s+.+?\n\s+.+?\n\s+}\n/,
|
|
||||||
"properties: comcord.clientSpoof.superProperties\n"
|
|
||||||
)
|
|
||||||
.replace(/\s+intents: this.client.shards.options.intents,/, "");
|
|
||||||
if (identifyFunction === newIdentify)
|
|
||||||
throw new Error("Failed to patch identify");
|
|
||||||
shard.identify = new Function(
|
|
||||||
`(function ${newIdentify}).apply(this, arguments)`
|
|
||||||
);
|
|
||||||
shard._wsEvent = shard.wsEvent;
|
|
||||||
shard.wsEvent = function (packet) {
|
|
||||||
if (packet.t == "READY") {
|
|
||||||
packet.d.application = {id: CLIENT_ID, flags: 565248};
|
|
||||||
}
|
|
||||||
|
|
||||||
const ret = this._wsEvent.apply(this, [packet]);
|
|
||||||
|
|
||||||
if (packet.t == "READY") {
|
|
||||||
for (const guild of packet.d.guilds) {
|
|
||||||
this._wsEvent.apply(this, [
|
|
||||||
{
|
|
||||||
t: "GUILD_CREATE",
|
|
||||||
d: guild,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("% Connecting to gateway now");
|
|
||||||
await client.connect();
|
|
||||||
})();
|
|
||||||
} else {
|
|
||||||
client.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("COMcord (c)left 2022");
|
|
||||||
console.log("Type 'h' for Commands");
|
|
||||||
|
|
||||||
const dateObj = new Date();
|
|
||||||
let sentTime = false;
|
|
||||||
|
|
||||||
const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
||||||
const months = [
|
|
||||||
"Jan",
|
|
||||||
"Feb",
|
|
||||||
"Mar",
|
|
||||||
"Apr",
|
|
||||||
"May",
|
|
||||||
"Jun",
|
|
||||||
"Jul",
|
|
||||||
"Aug",
|
|
||||||
"Sep",
|
|
||||||
"Oct",
|
|
||||||
"Nov",
|
|
||||||
"Dec",
|
|
||||||
];
|
|
||||||
|
|
||||||
setInterval(function () {
|
|
||||||
dateObj.setTime(Date.now());
|
|
||||||
|
|
||||||
const hour = dateObj.getUTCHours(),
|
|
||||||
minutes = dateObj.getUTCMinutes(),
|
|
||||||
seconds = dateObj.getUTCSeconds(),
|
|
||||||
day = dateObj.getUTCDate(),
|
|
||||||
month = dateObj.getUTCMonth(),
|
|
||||||
year = dateObj.getUTCFullYear(),
|
|
||||||
weekDay = dateObj.getUTCDay();
|
|
||||||
|
|
||||||
const timeString = `[${weekdays[weekDay]} ${day
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}-${months[month]}-${year
|
|
||||||
.toString()
|
|
||||||
.substring(2, 4)} ${hour.toString().padStart(2, "0")}:${minutes
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}]`;
|
|
||||||
|
|
||||||
if (minutes % 15 == 0 && seconds < 2 && !sentTime) {
|
|
||||||
if (comcord.state.inPrompt == true) {
|
|
||||||
comcord.state.messageQueue.push({time: true, content: timeString});
|
|
||||||
} else {
|
|
||||||
console.log(timeString);
|
|
||||||
}
|
|
||||||
comcord.state.nameLength = (client.user?.username?.length ?? 0) + 2;
|
|
||||||
sentTime = true;
|
|
||||||
} else if (seconds > 2 && sentTime) {
|
|
||||||
sentTime = false;
|
|
||||||
}
|
|
||||||
}, 500);
|
|
|
@ -1,133 +0,0 @@
|
||||||
/*
|
|
||||||
* This single file is **EXCLUDED** from the project license.
|
|
||||||
*
|
|
||||||
* (c) 2022 Cynthia Foxwell, all rights reserved.
|
|
||||||
* Permission is hereby granted to redistribute this file ONLY with copies of comcord.
|
|
||||||
* You may not reverse engineer, modify, copy, or redistribute this file for any other uses outside of comcord.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const os = require("os");
|
|
||||||
|
|
||||||
async function fetchMainPage() {
|
|
||||||
const res = await fetch("https://discord.com/channels/@me");
|
|
||||||
return await res.text();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchAsset(assetPath) {
|
|
||||||
return await fetch("https://discord.com/" + assetPath).then((res) =>
|
|
||||||
res.text()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const MATCH_SCRIPT = '<script src="(.+?)" integrity=".+?">';
|
|
||||||
const REGEX_SCRIPT = new RegExp(MATCH_SCRIPT);
|
|
||||||
const REGEX_SCRIPT_GLOBAL = new RegExp(MATCH_SCRIPT, "g");
|
|
||||||
|
|
||||||
async function extractScripts() {
|
|
||||||
const mainPage = await fetchMainPage();
|
|
||||||
|
|
||||||
return mainPage
|
|
||||||
.match(REGEX_SCRIPT_GLOBAL)
|
|
||||||
.map((script) => script.match(REGEX_SCRIPT)[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const REGEX_BUILD_NUMBER = /Build Number: (\d+), Version Hash:/;
|
|
||||||
const REGEX_BUILD_NUMBER_SWC = /Build Number: "\).concat\("(\d+)"/;
|
|
||||||
|
|
||||||
async function getBuildNumber() {
|
|
||||||
if (comcord.state.cachedBuildNumber) {
|
|
||||||
return comcord.state.cachedBuildNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scripts = await extractScripts();
|
|
||||||
const chunkWithBuildInfoAsset = scripts[3];
|
|
||||||
const chunkWithBuildInfo = await fetchAsset(chunkWithBuildInfoAsset);
|
|
||||||
|
|
||||||
const buildNumber =
|
|
||||||
chunkWithBuildInfo.match(REGEX_BUILD_NUMBER_SWC)?.[1] ??
|
|
||||||
chunkWithBuildInfo.match(REGEX_BUILD_NUMBER)?.[1];
|
|
||||||
comcord.state.cachedBuildNumber = buildNumber;
|
|
||||||
|
|
||||||
return buildNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*async function getClientVersion() {
|
|
||||||
if (comcord.state.cachedClientVersion) {
|
|
||||||
return comcord.state.cachedClientVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await fetch(
|
|
||||||
"https://updates.discord.com/distributions/app/manifests/latest?channel=stable&platform=win&arch=x86"
|
|
||||||
).then((res) => res.json());
|
|
||||||
const clientVersion = data.full.host_version.join(".");
|
|
||||||
comcord.state.cachedClientVersion = clientVersion;
|
|
||||||
|
|
||||||
return clientVersion;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
async function getBrowserInfo() {
|
|
||||||
let targetOS;
|
|
||||||
switch (process.platform) {
|
|
||||||
case "win32":
|
|
||||||
default:
|
|
||||||
targetOS = "windows";
|
|
||||||
break;
|
|
||||||
case "darwin":
|
|
||||||
targetOS = "mac os";
|
|
||||||
break;
|
|
||||||
case "linux":
|
|
||||||
targetOS = "linux";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await fetch(
|
|
||||||
`https://cdn.jsdelivr.net/gh/ray-lothian/UserAgent-Switcher/v2/firefox/data/popup/browsers/firefox-${encodeURIComponent(
|
|
||||||
targetOS
|
|
||||||
)}.json`
|
|
||||||
).then((res) => res.json());
|
|
||||||
data.sort((a, b) => Number(b.browser.major) - Number(a.browser.major));
|
|
||||||
const target = data[0];
|
|
||||||
|
|
||||||
return {ua: target.ua, version: target.browser.version};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSuperProperties() {
|
|
||||||
const buildNumber = await getBuildNumber();
|
|
||||||
// const clientVersion = await getClientVersion();
|
|
||||||
const browserInfo = await getBrowserInfo();
|
|
||||||
|
|
||||||
let _os;
|
|
||||||
switch (process.platform) {
|
|
||||||
case "win32":
|
|
||||||
default:
|
|
||||||
_os = "Windows";
|
|
||||||
break;
|
|
||||||
case "darwin":
|
|
||||||
_os = "Mac OS X";
|
|
||||||
break;
|
|
||||||
case "linux":
|
|
||||||
_os = "Linux";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
browser: "Firefox",
|
|
||||||
browser_user_agent: browserInfo.ua,
|
|
||||||
browser_version: browserInfo.version,
|
|
||||||
client_build_number: buildNumber,
|
|
||||||
client_event_source: null,
|
|
||||||
device: "",
|
|
||||||
os: _os,
|
|
||||||
os_version: os.release(),
|
|
||||||
//os_arch: os.arch(),
|
|
||||||
referrer: "",
|
|
||||||
referrer_current: "",
|
|
||||||
referring_domain: "",
|
|
||||||
referring_domain_current: "",
|
|
||||||
release_channel: "stable",
|
|
||||||
system_locale: "en-US",
|
|
||||||
};
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {getSuperProperties};
|
|
|
@ -1,17 +0,0 @@
|
||||||
function addCommand(key, name, callback) {
|
|
||||||
if (comcord.commands[key]) {
|
|
||||||
console.error(
|
|
||||||
`Registering duplicate key for "${key}": "${name}" wants to overwrite "${comcord.commands[key].name}"!`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
comcord.commands[key] = {
|
|
||||||
name,
|
|
||||||
callback,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
addCommand,
|
|
||||||
};
|
|
|
@ -1,566 +0,0 @@
|
||||||
const {Constants} = require("@projectdysnomia/dysnomia");
|
|
||||||
const chalk = require("chalk");
|
|
||||||
|
|
||||||
const REGEX_CODEBLOCK = /```(?:([a-z0-9_+\-.]+?)\n)?\n*([^\n][^]*?)\n*```/i;
|
|
||||||
const REGEX_CODEBLOCK_GLOBAL =
|
|
||||||
/```(?:[a-z0-9_+\-.]+?\n)?\n*([^\n][^]*?)\n*```/gi;
|
|
||||||
|
|
||||||
const REGEX_MENTION = /<@!?(\d+)>/g;
|
|
||||||
const REGEX_ROLE_MENTION = /<@&?(\d+)>/g;
|
|
||||||
const REGEX_CHANNEL = /<#(\d+)>/g;
|
|
||||||
const REGEX_EMOTE = /<(?:\u200b|&)?a?:(\w+):(\d+)>/g;
|
|
||||||
const REGEX_COMMAND = /<\/([^\s]+?):(\d+)>/g;
|
|
||||||
|
|
||||||
const REGEX_BLOCKQUOTE = /^ *>>?>? +/;
|
|
||||||
const REGEX_GREENTEXT = /^(>.+?)(?:\n|$)/;
|
|
||||||
const REGEX_SPOILER = /\|\|(.+?)\|\|/;
|
|
||||||
const REGEX_BOLD = /\*\*(.+?)\*\*/g;
|
|
||||||
const REGEX_UNDERLINE = /__(.+?)__/g;
|
|
||||||
const REGEX_ITALIC_1 = /\*(.+?)\*/g;
|
|
||||||
const REGEX_ITALIC_2 = /_(.+?)_/g;
|
|
||||||
const REGEX_STRIKE = /~~(.+?)~~/g;
|
|
||||||
const REGEX_3Y3 = /[\u{e0020}-\u{e007e}]{1,}/gu;
|
|
||||||
|
|
||||||
function readableTime(time) {
|
|
||||||
const seconds = time / 1000;
|
|
||||||
const minutes = seconds / 60;
|
|
||||||
const hours = minutes / 60;
|
|
||||||
const days = hours / 24;
|
|
||||||
const weeks = days / 7;
|
|
||||||
const months = days / 30;
|
|
||||||
const years = days / 365.25;
|
|
||||||
|
|
||||||
if (years >= 1) {
|
|
||||||
return `${years.toFixed(0)} year${years > 1 ? "s" : ""}`;
|
|
||||||
} else if (weeks > 5 && months < 13) {
|
|
||||||
return `${months.toFixed(0)} month${months > 1 ? "s" : ""}`;
|
|
||||||
} else if (days > 7 && weeks < 5) {
|
|
||||||
return `${weeks.toFixed(0)} week${weeks > 1 ? "s" : ""}`;
|
|
||||||
} else if (hours > 24 && days < 7) {
|
|
||||||
return `${days.toFixed(0)} day${days > 1 ? "s" : ""}`;
|
|
||||||
} else if (minutes > 60 && hours < 24) {
|
|
||||||
return `${hours.toFixed(0)} hour${hours > 1 ? "s" : ""}`;
|
|
||||||
} else if (seconds > 60 && minutes < 60) {
|
|
||||||
return `${minutes.toFixed(0)} minute${minutes > 1 ? "s" : ""}`;
|
|
||||||
} else {
|
|
||||||
return `${seconds.toFixed(0)} second${seconds > 1 ? "s" : ""}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MONTH_NAMES = [
|
|
||||||
"January",
|
|
||||||
"Feburary",
|
|
||||||
"March",
|
|
||||||
"April",
|
|
||||||
"May",
|
|
||||||
"June",
|
|
||||||
"July",
|
|
||||||
"August",
|
|
||||||
"September",
|
|
||||||
"October",
|
|
||||||
"November",
|
|
||||||
"December",
|
|
||||||
];
|
|
||||||
const DAY_NAMES = [
|
|
||||||
"Sunday",
|
|
||||||
"Monday",
|
|
||||||
"Tuesday",
|
|
||||||
"Wednesday",
|
|
||||||
"Thursday",
|
|
||||||
"Friday",
|
|
||||||
"Saturday",
|
|
||||||
];
|
|
||||||
const TIME_FORMATS = {
|
|
||||||
t: function (time) {
|
|
||||||
const timeObj = new Date(time);
|
|
||||||
return timeObj.getUTCHours() + 1 + ":" + timeObj.getUTCMinutes();
|
|
||||||
},
|
|
||||||
T: function (time) {
|
|
||||||
const timeObj = new Date(time);
|
|
||||||
return TIME_FORMATS.t(time) + ":" + timeObj.getUTCSeconds();
|
|
||||||
},
|
|
||||||
d: function (time) {
|
|
||||||
const timeObj = new Date(time);
|
|
||||||
return (
|
|
||||||
timeObj.getUTCFullYear() +
|
|
||||||
"/" +
|
|
||||||
(timeObj.getUTCMonth() + 1).toString().padStart(2, "0") +
|
|
||||||
"/" +
|
|
||||||
timeObj.getUTCDate().toString().padStart(2, "0")
|
|
||||||
);
|
|
||||||
},
|
|
||||||
D: function (time) {
|
|
||||||
const timeObj = new Date(time);
|
|
||||||
return (
|
|
||||||
timeObj.getUTCDate() +
|
|
||||||
" " +
|
|
||||||
MONTH_NAMES[timeObj.getUTCMonth()] +
|
|
||||||
" " +
|
|
||||||
timeObj.getUTCFullYear()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
f: function (time) {
|
|
||||||
return TIME_FORMATS.D(time) + " " + TIME_FORMATS.t(time);
|
|
||||||
},
|
|
||||||
F: function (time) {
|
|
||||||
const timeObj = new Date(time);
|
|
||||||
return DAY_NAMES[timeObj.getUTCDay()] + ", " + TIME_FORMATS.f(time);
|
|
||||||
},
|
|
||||||
R: function (time) {
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
if (time > now) {
|
|
||||||
const delta = time - now;
|
|
||||||
return "in " + readableTime(delta);
|
|
||||||
} else {
|
|
||||||
const delta = now - time;
|
|
||||||
return readableTime(delta) + " ago";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const REGEX_TIMESTAMP = new RegExp(
|
|
||||||
`<t:(-?\\d{1,17})(?::(${Object.keys(TIME_FORMATS).join("|")}))?>`,
|
|
||||||
"g"
|
|
||||||
);
|
|
||||||
|
|
||||||
function replaceMentions(_, id) {
|
|
||||||
const user = comcord.client.users.get(id);
|
|
||||||
if (user) {
|
|
||||||
return `@${user.username}`;
|
|
||||||
} else {
|
|
||||||
return "@Unknown User";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function replaceRoles(_, id) {
|
|
||||||
const role = comcord.client.guilds
|
|
||||||
.get(comcord.state.currentGuild)
|
|
||||||
.roles.get(id);
|
|
||||||
if (role) {
|
|
||||||
return `[@${role.name}]`;
|
|
||||||
} else {
|
|
||||||
return "[@Unknown Role]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function replaceChannels(_, id) {
|
|
||||||
const guildForChannel = comcord.client.channelGuildMap[id];
|
|
||||||
if (guildForChannel) {
|
|
||||||
const channel = comcord.client.guilds.get(guildForChannel).channels.get(id);
|
|
||||||
if (channel) {
|
|
||||||
return `#${channel.name}`;
|
|
||||||
} else {
|
|
||||||
return "#unknown-channel";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return "#unknown-channel";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function replaceEmotes(_, name, id) {
|
|
||||||
return `:${name}:`;
|
|
||||||
}
|
|
||||||
function replaceCommands(_, name, id) {
|
|
||||||
return `/${name}`;
|
|
||||||
}
|
|
||||||
function replaceTimestamps(_, time, format = "f") {
|
|
||||||
return TIME_FORMATS[format](time * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceStyledMarkdown(content) {
|
|
||||||
content = content.replace(REGEX_BLOCKQUOTE, chalk.blackBright("\u258e"));
|
|
||||||
content = content.replace(REGEX_GREENTEXT, (orig) => chalk.green(orig));
|
|
||||||
|
|
||||||
if (comcord.config.enable3y3) {
|
|
||||||
content = content.replace(REGEX_3Y3, (text) =>
|
|
||||||
chalk.italic.magenta(
|
|
||||||
[...text]
|
|
||||||
.map((char) => String.fromCodePoint(char.codePointAt(0) - 0xe0000))
|
|
||||||
.join("")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
content = content.replace(REGEX_SPOILER, (_, text) =>
|
|
||||||
chalk.bgBlack.black(text)
|
|
||||||
);
|
|
||||||
content = content.replace(REGEX_STRIKE, (_, text) =>
|
|
||||||
chalk.strikethrough(text)
|
|
||||||
);
|
|
||||||
content = content.replace(REGEX_BOLD, (_, text) => chalk.bold(text));
|
|
||||||
content = content.replace(REGEX_UNDERLINE, (_, text) =>
|
|
||||||
chalk.underline(text)
|
|
||||||
);
|
|
||||||
content = content
|
|
||||||
.replace(REGEX_ITALIC_1, (_, text) => chalk.italic(text))
|
|
||||||
.replace(REGEX_ITALIC_2, (_, text) => chalk.italic(text));
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatMessage({
|
|
||||||
channel,
|
|
||||||
name,
|
|
||||||
content,
|
|
||||||
bot,
|
|
||||||
attachments,
|
|
||||||
stickers,
|
|
||||||
reply,
|
|
||||||
timestamp,
|
|
||||||
mention = false,
|
|
||||||
noColor = false,
|
|
||||||
dump = false,
|
|
||||||
history = false,
|
|
||||||
dm = false,
|
|
||||||
join = false,
|
|
||||||
pin = false,
|
|
||||||
}) {
|
|
||||||
const dateObj = new Date(timestamp);
|
|
||||||
const hour = dateObj.getUTCHours().toString().padStart(2, "0"),
|
|
||||||
minutes = dateObj.getUTCMinutes().toString().padStart(2, "0"),
|
|
||||||
seconds = dateObj.getUTCSeconds().toString().padStart(2, "0");
|
|
||||||
|
|
||||||
let console = global.console;
|
|
||||||
const lines = [];
|
|
||||||
if (history) {
|
|
||||||
console = {
|
|
||||||
log: function (...args) {
|
|
||||||
lines.push(...args.join(" ").split("\n"));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.length + 2 > comcord.state.nameLength)
|
|
||||||
comcord.state.nameLength = name.length + 2;
|
|
||||||
|
|
||||||
if (reply) {
|
|
||||||
const nameColor = reply.author.bot ? chalk.bold.yellow : chalk.bold.cyan;
|
|
||||||
|
|
||||||
const headerLength = 5 + reply.author.username.length;
|
|
||||||
|
|
||||||
let replyContent = reply.content.replace(/\n/g, " ");
|
|
||||||
replyContent = replyContent
|
|
||||||
.replace(REGEX_MENTION, replaceMentions)
|
|
||||||
.replace(REGEX_ROLE_MENTION, replaceRoles)
|
|
||||||
.replace(REGEX_CHANNEL, replaceChannels)
|
|
||||||
.replace(REGEX_EMOTE, replaceEmotes)
|
|
||||||
.replace(REGEX_COMMAND, replaceCommands)
|
|
||||||
.replace(REGEX_TIMESTAMP, replaceTimestamps);
|
|
||||||
|
|
||||||
if (!noColor) {
|
|
||||||
replyContent = replaceStyledMarkdown(replyContent);
|
|
||||||
} else {
|
|
||||||
if (comcord.config.enable3y3) {
|
|
||||||
replyContent = replyContent.replace(
|
|
||||||
REGEX_3Y3,
|
|
||||||
(text) =>
|
|
||||||
`<3y3:${[...text]
|
|
||||||
.map((char) =>
|
|
||||||
String.fromCodePoint(char.codePointAt(0) - 0xe0000)
|
|
||||||
)
|
|
||||||
.join("")}>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reply.attachments.size > 0) {
|
|
||||||
replyContent += ` <${reply.attachments.size} attachment${
|
|
||||||
reply.attachments.size > 1 ? "s" : ""
|
|
||||||
}>`;
|
|
||||||
replyContent = replyContent.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
const length = headerLength + replyContent.length;
|
|
||||||
|
|
||||||
if (noColor) {
|
|
||||||
console.log(
|
|
||||||
` \u250d [${reply.author.username}] ${
|
|
||||||
length > 79
|
|
||||||
? replyContent.substring(0, 79 - headerLength) + "\u2026"
|
|
||||||
: replyContent
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
chalk.bold.white(" \u250d ") +
|
|
||||||
nameColor(`[${reply.author.username}] `) +
|
|
||||||
`${
|
|
||||||
length > 79
|
|
||||||
? replyContent.substring(0, 79 - headerLength) +
|
|
||||||
chalk.reset("\u2026")
|
|
||||||
: replyContent
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dump) {
|
|
||||||
if (history) {
|
|
||||||
const headerLength = 80 - (name.length + 5);
|
|
||||||
console.log(`--- ${name} ${"-".repeat(headerLength)}`);
|
|
||||||
console.log(content);
|
|
||||||
console.log(`--- ${name} ${"-".repeat(headerLength)}`);
|
|
||||||
} else {
|
|
||||||
const wordCount = content.split(" ").length;
|
|
||||||
const lineCount = content.split("\n").length;
|
|
||||||
if (noColor) {
|
|
||||||
console.log(
|
|
||||||
`<${name} DUMPs in ${content.length} characters of ${wordCount} word${
|
|
||||||
wordCount > 1 ? "s" : ""
|
|
||||||
} in ${lineCount} line${lineCount > 1 ? "s" : ""}>`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
chalk.bold.yellow(
|
|
||||||
`<${name} DUMPs in ${
|
|
||||||
content.length
|
|
||||||
} characters of ${wordCount} word${
|
|
||||||
wordCount > 1 ? "s" : ""
|
|
||||||
} in ${lineCount} line${lineCount > 1 ? "s" : ""}>`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content = content
|
|
||||||
.replace(REGEX_MENTION, replaceMentions)
|
|
||||||
.replace(REGEX_ROLE_MENTION, replaceRoles)
|
|
||||||
.replace(REGEX_CHANNEL, replaceChannels)
|
|
||||||
.replace(REGEX_EMOTE, replaceEmotes)
|
|
||||||
.replace(REGEX_COMMAND, replaceCommands)
|
|
||||||
.replace(REGEX_TIMESTAMP, replaceTimestamps);
|
|
||||||
|
|
||||||
if (dm) {
|
|
||||||
if (noColor) {
|
|
||||||
if (comcord.config.enable3y3) {
|
|
||||||
content = content.replace(
|
|
||||||
REGEX_3Y3,
|
|
||||||
(text) =>
|
|
||||||
`<3y3:${[...text]
|
|
||||||
.map((char) =>
|
|
||||||
String.fromCodePoint(char.codePointAt(0) - 0xe0000)
|
|
||||||
)
|
|
||||||
.join("")}>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`*${name}* ${content}\x07`);
|
|
||||||
} else {
|
|
||||||
content = replaceStyledMarkdown(content);
|
|
||||||
|
|
||||||
console.log(`${chalk.bold.red(`*${name}*`)} ${content}\x07`);
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
content.length > 1 &&
|
|
||||||
((content.startsWith("*") &&
|
|
||||||
content.endsWith("*") &&
|
|
||||||
!content.startsWith("**") &&
|
|
||||||
!content.endsWith("**")) ||
|
|
||||||
(content.startsWith("_") &&
|
|
||||||
content.endsWith("_") &&
|
|
||||||
!content.startsWith("__") &&
|
|
||||||
!content.endsWith("__")))
|
|
||||||
) {
|
|
||||||
if (comcord.config.enable3y3) {
|
|
||||||
content = content.replace(
|
|
||||||
REGEX_3Y3,
|
|
||||||
(text) =>
|
|
||||||
`<3y3:${[...text]
|
|
||||||
.map((char) =>
|
|
||||||
String.fromCodePoint(char.codePointAt(0) - 0xe0000)
|
|
||||||
)
|
|
||||||
.join("")}>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const str = `<${name} ${content.substring(1, content.length - 1)}>`;
|
|
||||||
if (noColor) {
|
|
||||||
console.log(str);
|
|
||||||
} else {
|
|
||||||
console.log(chalk.bold.green(str));
|
|
||||||
}
|
|
||||||
} else if (join) {
|
|
||||||
const str = `[${hour}:${minutes}:${seconds}] ${name} has joined ${channel.guild.name}`;
|
|
||||||
if (noColor) {
|
|
||||||
console.log(str);
|
|
||||||
} else {
|
|
||||||
console.log(chalk.bold.yellow(str));
|
|
||||||
}
|
|
||||||
} else if (pin) {
|
|
||||||
const str = `[${hour}:${minutes}:${seconds}] ${name} pinned a message to this channel`;
|
|
||||||
if (noColor) {
|
|
||||||
console.log(str);
|
|
||||||
} else {
|
|
||||||
console.log(chalk.bold.yellow(str));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (noColor) {
|
|
||||||
if (comcord.config.enable3y3) {
|
|
||||||
content = content.replace(
|
|
||||||
REGEX_3Y3,
|
|
||||||
(text) =>
|
|
||||||
`<3y3:${[...text]
|
|
||||||
.map((char) =>
|
|
||||||
String.fromCodePoint(char.codePointAt(0) - 0xe0000)
|
|
||||||
)
|
|
||||||
.join("")}>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`[${name}]${" ".repeat(
|
|
||||||
Math.abs(comcord.state.nameLength - (name.length + 2))
|
|
||||||
)} ${content}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const nameColor = mention
|
|
||||||
? chalk.bold.red
|
|
||||||
: bot
|
|
||||||
? chalk.bold.yellow
|
|
||||||
: chalk.bold.cyan;
|
|
||||||
|
|
||||||
content = replaceStyledMarkdown(content);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`${nameColor(`[${name}]`)}${" ".repeat(
|
|
||||||
Math.abs(comcord.state.nameLength - (name.length + 2))
|
|
||||||
)} ${content}${mention ? "\x07" : ""}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attachments) {
|
|
||||||
for (const attachment of attachments.values()) {
|
|
||||||
if (noColor) {
|
|
||||||
console.log(`<attachment: ${attachment.url} >`);
|
|
||||||
} else {
|
|
||||||
console.log(chalk.bold.yellow(`<attachment: ${attachment.url} >`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stickers) {
|
|
||||||
for (const sticker of stickers) {
|
|
||||||
if (noColor) {
|
|
||||||
console.log(
|
|
||||||
`<sticker: "${sticker.name}" https://media.discordapp.net/stickers/${sticker.id}.png >`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
chalk.bold.yellow(
|
|
||||||
`<sticker: "${sticker.name}" https://media.discordapp.net/stickers/${sticker.id}.png >`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (history) {
|
|
||||||
return lines;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function processMessage(msg, options = {}) {
|
|
||||||
if (
|
|
||||||
msg.channel?.type === Constants.ChannelTypes.DM ||
|
|
||||||
msg.channel?.type === Constants.ChannelTypes.GROUP_DM
|
|
||||||
) {
|
|
||||||
options.dm = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.type === Constants.MessageTypes.USER_JOIN) {
|
|
||||||
options.join = true;
|
|
||||||
} else if (msg.type === Constants.MessageTypes.CHANNEL_PINNED_MESSAGE) {
|
|
||||||
options.pin = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.time) {
|
|
||||||
console.log(msg.content);
|
|
||||||
return null;
|
|
||||||
} else if (msg.ping) {
|
|
||||||
console.log(
|
|
||||||
chalk.bold.red(
|
|
||||||
`**mentioned by ${msg.author?.username ?? "<unknown>"} in #${
|
|
||||||
msg.channel?.name ?? "<unknown>"
|
|
||||||
} in ${msg.channel.guild?.name ?? "<unknown>"}**\x07`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
} else if (msg.content && msg.content.indexOf("\n") > -1) {
|
|
||||||
if (msg.content.match(REGEX_CODEBLOCK)) {
|
|
||||||
return formatMessage({
|
|
||||||
channel: msg.channel,
|
|
||||||
name: msg.author.username,
|
|
||||||
bot: msg.author.bot,
|
|
||||||
content: msg.content.replace(
|
|
||||||
REGEX_CODEBLOCK_GLOBAL,
|
|
||||||
(_, content) => content
|
|
||||||
),
|
|
||||||
attachments: msg.attachments,
|
|
||||||
stickers: msg.stickerItems,
|
|
||||||
reply: msg.referencedMessage,
|
|
||||||
timestamp: msg.timestamp,
|
|
||||||
mention:
|
|
||||||
msg.mentionsEveryone ||
|
|
||||||
msg.mentions.find((user) => user.id == comcord.client.user.id),
|
|
||||||
dump: true,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const lines = msg.content.split("\n");
|
|
||||||
const outLines = [];
|
|
||||||
for (const index in lines) {
|
|
||||||
const line = lines[index];
|
|
||||||
outLines.push(
|
|
||||||
formatMessage({
|
|
||||||
channel: msg.channel,
|
|
||||||
name: msg.author.username,
|
|
||||||
bot: msg.author.bot,
|
|
||||||
content:
|
|
||||||
line +
|
|
||||||
(msg.editedTimestamp != null && index == lines.length - 1
|
|
||||||
? " (edited)"
|
|
||||||
: ""),
|
|
||||||
attachments: index == lines.length - 1 ? msg.attachments : [],
|
|
||||||
stickers: index == lines.length - 1 ? msg.stickerItems : [],
|
|
||||||
reply: index == 0 ? msg.referencedMessage : null,
|
|
||||||
timestamp: msg.timestamp,
|
|
||||||
mention:
|
|
||||||
index == 0 &&
|
|
||||||
(msg.mentionsEveryone ||
|
|
||||||
msg.mentions.find((user) => user.id == comcord.client.user.id)),
|
|
||||||
...options,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return outLines;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return formatMessage({
|
|
||||||
channel: msg.channel,
|
|
||||||
name: msg.author.username,
|
|
||||||
bot: msg.author.bot,
|
|
||||||
content: msg.content + (msg.editedTimestamp != null ? " (edited)" : ""),
|
|
||||||
attachments: msg.attachments,
|
|
||||||
stickers: msg.stickerItems,
|
|
||||||
reply: msg.referencedMessage,
|
|
||||||
timestamp: msg.timestamp,
|
|
||||||
mention:
|
|
||||||
msg.mentionsEveryone ||
|
|
||||||
msg.mentions.find((user) => user.id == comcord.client.user.id),
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function processQueue() {
|
|
||||||
for (const msg of comcord.state.messageQueue) {
|
|
||||||
processMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
comcord.state.messageQueue.splice(0, comcord.state.messageQueue.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
processMessage,
|
|
||||||
processQueue,
|
|
||||||
formatMessage,
|
|
||||||
};
|
|
|
@ -1,89 +0,0 @@
|
||||||
const CLIENT_ID = "1026163285877325874";
|
|
||||||
|
|
||||||
function updatePresence() {
|
|
||||||
let guild, channel;
|
|
||||||
if (comcord.state.currentGuild != null) {
|
|
||||||
guild = comcord.client.guilds.get(comcord.state.currentGuild);
|
|
||||||
}
|
|
||||||
if (comcord.state.currentChannel != null && guild != null) {
|
|
||||||
channel = guild.channels.get(comcord.state.currentChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comcord.client.user.bot) {
|
|
||||||
if (comcord.state.rpcConnected) {
|
|
||||||
try {
|
|
||||||
const activity = {
|
|
||||||
startTimestamp: comcord.state.startTime,
|
|
||||||
smallImageKey: `https://cdn.discordapp.com/avatars/${comcord.client.user.id}/${comcord.client.user.avatar}.png?size=1024`,
|
|
||||||
smallImageText: comcord.client.user.username,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: "comcord Repo",
|
|
||||||
url: "https://github.com/Cynosphere/comcord",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (guild != null) {
|
|
||||||
activity.largeImageKey = `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.png?size=1024`;
|
|
||||||
activity.largeImageText = guild.name;
|
|
||||||
if (channel != null) {
|
|
||||||
activity.details = `#${channel.name} - ${guild.name}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (comcord.state.afk == true) {
|
|
||||||
activity.state = "AFK";
|
|
||||||
}
|
|
||||||
comcord.rpc.setActivity(activity);
|
|
||||||
} catch (err) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
comcord.client.editStatus(
|
|
||||||
comcord.state.afk ? "idle" : comcord.config.defaultStatus ?? "online",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: "comcord",
|
|
||||||
type: 0,
|
|
||||||
application_id: CLIENT_ID,
|
|
||||||
timestamps: {
|
|
||||||
start: comcord.state.startTime,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const activity = {
|
|
||||||
application_id: CLIENT_ID,
|
|
||||||
name: "comcord",
|
|
||||||
timestamps: {
|
|
||||||
start: comcord.state.startTime,
|
|
||||||
},
|
|
||||||
assets: {},
|
|
||||||
buttons: ["comcord Repo"],
|
|
||||||
metadata: {
|
|
||||||
button_urls: ["https://github.com/Cynosphere/comcord"],
|
|
||||||
},
|
|
||||||
type: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (guild != null) {
|
|
||||||
activity.assets.large_image = `mp:icons/${guild.id}/${guild.icon}.png?size=1024`;
|
|
||||||
activity.assets.large_text = guild.name;
|
|
||||||
if (channel != null) {
|
|
||||||
activity.details = `#${channel.name} - ${guild.name}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (comcord.state.afk == true) {
|
|
||||||
activity.state = "AFK";
|
|
||||||
}
|
|
||||||
|
|
||||||
comcord.client.editStatus(
|
|
||||||
comcord.state.afk ? "idle" : comcord.config.defaultStatus ?? "online",
|
|
||||||
[activity]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {updatePresence};
|
|
|
@ -1,22 +0,0 @@
|
||||||
function startPrompt(display, callback) {
|
|
||||||
comcord.state.inPrompt = true;
|
|
||||||
comcord.state.promptText = display;
|
|
||||||
comcord.state.promptInput = "";
|
|
||||||
|
|
||||||
comcord.state.promptCallback = callback;
|
|
||||||
|
|
||||||
process.stdout.write(display);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function finalizePrompt() {
|
|
||||||
comcord.state.inPrompt = false;
|
|
||||||
comcord.state.promptText = null;
|
|
||||||
|
|
||||||
const input = comcord.state.promptInput.trim();
|
|
||||||
await comcord.state.promptCallback(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
startPrompt,
|
|
||||||
finalizePrompt,
|
|
||||||
};
|
|
|
@ -1,30 +0,0 @@
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
const os = require("os");
|
|
||||||
|
|
||||||
const RCPATH = path.resolve(os.homedir(), ".comcordrc");
|
|
||||||
|
|
||||||
function readFile(config) {
|
|
||||||
const rc = fs.readFileSync(RCPATH, "utf8");
|
|
||||||
const lines = rc.split("\n");
|
|
||||||
for (const line of lines) {
|
|
||||||
const [key, value] = line.split("=");
|
|
||||||
config[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeFile(config) {
|
|
||||||
if (fs.existsSync(RCPATH)) {
|
|
||||||
readFile(config);
|
|
||||||
}
|
|
||||||
const newrc = [];
|
|
||||||
|
|
||||||
for (const key in config) {
|
|
||||||
const value = config[key];
|
|
||||||
newrc.push(`${key}=${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(RCPATH, newrc.join("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {readFile, writeFile, path: RCPATH};
|
|
181
state/main.go
Normal file
181
state/main.go
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
|
"github.com/diamondburned/ningen/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComcordState struct {
|
||||||
|
Client *ningen.State
|
||||||
|
Config map[string]string
|
||||||
|
Readied bool
|
||||||
|
Connected bool
|
||||||
|
RPCConnected bool
|
||||||
|
StartTime time.Time
|
||||||
|
CurrentGuild string
|
||||||
|
CurrentChannel string
|
||||||
|
NameLength int
|
||||||
|
InPrompt bool
|
||||||
|
PromptText string
|
||||||
|
AFK bool
|
||||||
|
MessageQueue []discord.Message
|
||||||
|
LastChannel map[string]string
|
||||||
|
LastDM string
|
||||||
|
NoColor bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var state ComcordState
|
||||||
|
|
||||||
|
func Setup(config map[string]string, client *ningen.State) {
|
||||||
|
state = ComcordState{}
|
||||||
|
state.Client = client
|
||||||
|
state.Config = config
|
||||||
|
state.Readied = false
|
||||||
|
state.Connected = true
|
||||||
|
state.RPCConnected = false
|
||||||
|
state.StartTime = time.Now()
|
||||||
|
state.CurrentGuild = ""
|
||||||
|
state.CurrentChannel = ""
|
||||||
|
state.NameLength = 2
|
||||||
|
state.InPrompt = false
|
||||||
|
state.PromptText = ""
|
||||||
|
state.AFK = false
|
||||||
|
state.MessageQueue = make([]discord.Message, 0)
|
||||||
|
state.LastChannel = make(map[string]string)
|
||||||
|
state.LastDM = ""
|
||||||
|
state.NoColor = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClient() *ningen.State {
|
||||||
|
return state.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasReadied() bool {
|
||||||
|
return state.Readied
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetReadied(value bool) {
|
||||||
|
state.Readied = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsConnected() bool {
|
||||||
|
return state.Connected
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetConnected(value bool) {
|
||||||
|
state.Connected = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRPCConnected() bool {
|
||||||
|
return state.RPCConnected
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRPCConnected(value bool) {
|
||||||
|
state.RPCConnected = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStartTime() time.Time {
|
||||||
|
return state.StartTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCurrentGuild() string {
|
||||||
|
return state.CurrentGuild
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCurrentGuild(value string) {
|
||||||
|
state.CurrentGuild = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCurrentChannel() string {
|
||||||
|
return state.CurrentChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCurrentChannel(value string) {
|
||||||
|
state.CurrentChannel = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNameLength() int {
|
||||||
|
return state.NameLength
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetNameLength(value int) {
|
||||||
|
state.NameLength = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInPrompt() bool {
|
||||||
|
return state.InPrompt
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetInPrompt(value bool) {
|
||||||
|
state.InPrompt = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPromptText() string {
|
||||||
|
return state.PromptText
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetPromptText(value string) {
|
||||||
|
state.PromptText = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsAFK() bool {
|
||||||
|
return state.AFK
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetAFK(value bool) {
|
||||||
|
state.AFK = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMessageQueue() []discord.Message {
|
||||||
|
return state.MessageQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddMessageToQueue(msg discord.Message) {
|
||||||
|
state.MessageQueue = append(state.MessageQueue, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EmptyMessageQueue() {
|
||||||
|
state.MessageQueue = make([]discord.Message, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetLastChannel(guild string, channel string) {
|
||||||
|
state.LastChannel[guild] = channel
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLastChannel(guild string) string {
|
||||||
|
channel, has := state.LastChannel[guild]
|
||||||
|
|
||||||
|
if has {
|
||||||
|
return channel
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLastDM() string {
|
||||||
|
return state.LastDM
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetLastDM(value string) {
|
||||||
|
state.LastDM = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfigValue(key string) string {
|
||||||
|
value, has := state.Config[key]
|
||||||
|
|
||||||
|
if has {
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetNoColor(value bool) {
|
||||||
|
state.NoColor = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasNoColor() bool {
|
||||||
|
return state.NoColor
|
||||||
|
}
|
Loading…
Reference in a new issue