Compare commits
No commits in common. "rewrite-go" and "main" have entirely different histories.
rewrite-go
...
main
|
@ -0,0 +1,31 @@
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,2 +1 @@
|
||||||
comcord
|
node_modules/
|
||||||
comcord.exe
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
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 (`rewrite-go`)
|
# comcord
|
||||||
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,13 +6,30 @@ 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
|
||||||
TODO
|
1. `pnpm i`
|
||||||
|
2. `node src/index.js <token>`
|
||||||
|
|
||||||
## Rewrite Design Decisions
|
Your token will be then stored in `.comcordrc` after the first launch.
|
||||||
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)
|
||||||
|
@ -21,42 +38,41 @@ Go is more portable than Node.js
|
||||||
- [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 (<)
|
||||||
- [ ] AFK toggle (A)
|
- [x] AFK toggle (A)
|
||||||
- [ ] Send DM (s)
|
- [x] Send DM (s)
|
||||||
- [ ] Answer DM (a)
|
- [x] Answer DM (a)
|
||||||
- [x] Current time (+)
|
- [x] Peek (p)
|
||||||
- [ ] DM history (TBD)
|
|
||||||
- [ ] Reply to message (TBD)
|
|
||||||
- [ ] Toggle color (z)
|
|
||||||
- [x] Message Receiving
|
- [x] Message Receiving
|
||||||
- Markdown styling
|
- [x] Markdown styling
|
||||||
- [x] Emotes
|
- [x] Common markdown (bold, italic, etc)
|
||||||
|
- [x] Figure out how spoilers would work
|
||||||
|
- [x] Emotes?????
|
||||||
- [x] Timestamp parsing
|
- [x] Timestamp parsing
|
||||||
- [x] Mentions parsing
|
- [x] Mentions parsing
|
||||||
- [ ] Embeds
|
- [ ] Embeds in the style of commode's posted links
|
||||||
- [ ] 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
|
||||||
- [x] Send typing
|
- [ ] Mentions
|
||||||
- [ ] Mentioning
|
- [ ] Replies
|
||||||
- [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
|
||||||
- [ ] Threads/Forums
|
- No way to set in client (yet?), `defaultChannel=` and `defaultGuild=` in your `.comcordrc`.
|
||||||
- [ ] External rich presence when using bot accounts
|
- [ ] Threads
|
||||||
|
- [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.
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ClearCommand() {
|
|
||||||
fmt.Print("\n\r\033[H\033[2J")
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,658 +0,0 @@
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
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")
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
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
129
commands/send.go
|
@ -1,129 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
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]"))
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
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()
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
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
27
go.mod
|
@ -1,27 +0,0 @@
|
||||||
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
350
go.sum
|
@ -1,350 +0,0 @@
|
||||||
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
547
lib/messages.go
|
@ -1,547 +0,0 @@
|
||||||
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
121
lib/presence.go
|
@ -1,121 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
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
79
lib/util.go
|
@ -1,79 +0,0 @@
|
||||||
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
185
main.go
|
@ -1,185 +0,0 @@
|
||||||
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()*/
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
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
|
|
@ -1,47 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
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();
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
const {addCommand} = require("../lib/command");
|
||||||
|
|
||||||
|
addCommand("c", "clear", function () {
|
||||||
|
console.clear();
|
||||||
|
});
|
|
@ -0,0 +1,25 @@
|
||||||
|
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}>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
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");
|
||||||
|
});
|
|
@ -0,0 +1,126 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,55 @@
|
||||||
|
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,
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
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,
|
||||||
|
};
|
|
@ -0,0 +1,88 @@
|
||||||
|
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,
|
||||||
|
};
|
|
@ -0,0 +1,60 @@
|
||||||
|
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>");
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
const {addCommand} = require("../lib/command");
|
||||||
|
|
||||||
|
addCommand("q", "quit comcord", function () {
|
||||||
|
comcord.state.quitting = true;
|
||||||
|
comcord.client.disconnect({reconnect: false});
|
||||||
|
process.exit(0);
|
||||||
|
});
|
|
@ -0,0 +1,44 @@
|
||||||
|
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};
|
|
@ -0,0 +1,47 @@
|
||||||
|
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);
|
||||||
|
});
|
|
@ -0,0 +1,60 @@
|
||||||
|
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};
|
|
@ -0,0 +1,470 @@
|
||||||
|
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);
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* 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};
|
|
@ -0,0 +1,17 @@
|
||||||
|
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,
|
||||||
|
};
|
|
@ -0,0 +1,566 @@
|
||||||
|
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,
|
||||||
|
};
|
|
@ -0,0 +1,89 @@
|
||||||
|
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};
|
|
@ -0,0 +1,22 @@
|
||||||
|
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,
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
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
181
state/main.go
|
@ -1,181 +0,0 @@
|
||||||
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 New Issue