From 8144a7270afab2f8ff307511eca54b6b8c355418 Mon Sep 17 00:00:00 2001 From: Ringo Hoffmann Date: Fri, 11 Feb 2022 15:22:10 +0100 Subject: [PATCH 01/17] update changelog --- CHANGELOG.md | 48 +++--------------------------------------------- 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e313d24..41c50bd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,53 +1,11 @@ [VERSION] - - -# Birthday Notifications [#349] - -shinpuru now features birthday notifications! - -![](https://user-images.githubusercontent.com/16734205/153576590-28b51ce9-e11f-4aa1-b86b-41fc7d6d6a31.gif) - -Simply, set your birthday using the `/birthday set` slash command. The date must be in the format `YYYY-MM-DD[+-]TIMEZONE`. You can also use `/` or `.` as separator. The timezone must be set to your local time zone in offset hours from UTC. So, for example, for `CET` this would be `+1`, for `EST`, this would be `-5` and so on. - -It is also possible to just set a month and day if you don't want to store your birth year. If you want to show your age in the notification, you can enable it by setting `show-year` to `True`. Otherwise, your age will be hidden in the notification. - -![](https://user-images.githubusercontent.com/16734205/153576393-73616bd2-b21d-4813-bdb8-ad146cecc542.png) - -*With age hidden, it would look like this.* -![](https://user-images.githubusercontent.com/16734205/153580431-e5a0f4b9-1b51-473f-bfa4-40c3fa650c89.gif) - -You can, of course, also unset your birthday at any time. - -![](https://user-images.githubusercontent.com/16734205/153577075-e191e2d8-1e39-4ab4-9a24-31e5939d887f.png) - -To enable birthday notifications in your guild, you need to specify a birthday channel. This requires the permission `sp.guild.config.birthday` - -![](https://user-images.githubusercontent.com/16734205/153576456-9c184ae0-4408-4ded-9dce-9f69ee36f5e9.png) - -To unset this setting, simply use the `/birthday unset-cannel` command. - -To enable Gifs in the birthday message, the bot needs a Giphy API key. You can get one by creating a Giphy acount and going to the [Developer Dashboard](https://developers.giphy.com/dashboard/). There you can create an app. After that, copy the API key and add it ti shinpuru's config. -```yaml -giphy: - apikey: dWl3ZXF6diBzZGR0NnczNDg5NTZuZG4w -``` - -# Sharding [#238] - -If you are running shinpuru of a lot of Guilds, you might want to split up your single instance into multiple instances to split up the load. This is now possible using Discord's Gateway sharding. - -> I **strongly** recommend taking a look into [Discord's Documentation](https://discord.com/developers/docs/topics/gateway#sharding) about sharding when you want to split up your instance. - -You can simply spin up multiple instances of shinpuru behind a load balancer which all connect to the same database and redis instance. This distributes a common synced persistent state between all instances. - -If you want to set up sharding and load balancing, you can find more information on how to set up and configure shinpuru [**here**](https://github.com/zekroTJA/shinpuru/tree/dev/docs/sharding). There you can also find an example deploymet using docker swarm. +> **Attention** +> This is a hotfix patch. If you want to see the changelog for release 1.30.0, please look [**here**](https://github.com/zekroTJA/shinpuru/releases/tag/1.30.0). # Bug Fixes -- Fix a bug where guild settings were not saved to database. -- Properly bubble up errors when setting guild settings. +- Delete command message of `/birthday set` when `show-age` is set to `False`. # Docker From 8195f225e489f7090cc5eea5b6da70ca2ad465dc Mon Sep 17 00:00:00 2001 From: zordem <38507337+zordem@users.noreply.github.com> Date: Fri, 11 Feb 2022 15:33:38 +0100 Subject: [PATCH 02/17] fixed typo cannel => channel --- internal/slashcommands/birthday.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/slashcommands/birthday.go b/internal/slashcommands/birthday.go index 305359c2..111e99cc 100644 --- a/internal/slashcommands/birthday.go +++ b/internal/slashcommands/birthday.go @@ -47,7 +47,7 @@ func (c *Birthday) Options() []*discordgo.ApplicationCommandOption { return []*discordgo.ApplicationCommandOption{ { Type: discordgo.ApplicationCommandOptionSubCommand, - Name: "set-cannel", + Name: "set-channel", Description: "Set birthday message channel.", Options: []*discordgo.ApplicationCommandOption{ { @@ -63,7 +63,7 @@ func (c *Birthday) Options() []*discordgo.ApplicationCommandOption { }, { Type: discordgo.ApplicationCommandOptionSubCommand, - Name: "unset-cannel", + Name: "unset-channel", Description: "Unset birthday message channel.", }, { @@ -113,8 +113,8 @@ func (c *Birthday) Run(ctx *ken.Ctx) (err error) { } err = ctx.HandleSubCommands( - ken.SubCommandHandler{"set-cannel", c.setChannel}, - ken.SubCommandHandler{"unset-cannel", c.unsetChannel}, + ken.SubCommandHandler{"set-channel", c.setChannel}, + ken.SubCommandHandler{"unset-channel", c.unsetChannel}, ken.SubCommandHandler{"set", c.set}, ken.SubCommandHandler{"remove", c.remove}, ) From 43114b5a557e2ee453dfe45a89b53623314be578 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 11 Feb 2022 16:13:24 +0100 Subject: [PATCH 03/17] fixed typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d990e01..06a50e7c 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ The code is picked up and sent to a code execution engine, which safely executes ### Karma -shinpuru featues a Karma system which is inspired by Reddit. You can define specific emotes which, when attached to a message, increase or reduce the karma points of a member. You can also specify the amount of "tokens" which can be spent each hour as well as a penalty for giving negative karma, which also takes karma from the executor to prevent downvote spam. +shinpuru features a Karma system which is inspired by Reddit. You can define specific emotes which, when attached to a message, increase or reduce the karma points of a member. You can also specify the amount of "tokens" which can be spent each hour as well as a penalty for giving negative karma, which also takes karma from the executor to prevent downvote spam. It is also possible to execute actions when passing specific amounts of karma. For example, you can add or remove roles, send messages or even kick/ban members depending on their karma points. @@ -147,7 +147,7 @@ The last 10 backups are stored and can be reviewed in the web interface. ### Raid Alerting -This system allows you to set a threshold of new user ingress reate. When this rate exceeds, for example when a lot of (bot) accounts flush in to your guild (aka `raiding`), all admins of the guild will be allerted via DM. Also, the guilds moderation setting will be raised to `Highest` so that only users with roles or a valid phone number can chat. +This system allows you to set a threshold of new user ingress rate. When this rate exceeds, for example when a lot of (bot) accounts flush in to your guild (aka `raiding`), all admins of the guild will be alerted via DM. Also, the guilds moderation setting will be raised to `Highest` so that only users with roles or a valid phone number can chat. ![image](https://user-images.githubusercontent.com/16734205/140644018-9652d8c9-2716-43ae-bf5b-c1b2c17f895a.png) From 1ad8e77439be5d7f0336ecc560a242247bdda740 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 12 Feb 2022 00:38:21 +0000 Subject: [PATCH 04/17] update bughunters --- bughunters.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bughunters.md b/bughunters.md index 6bde70a3..f67d769d 100644 --- a/bughunters.md +++ b/bughunters.md @@ -2,7 +2,7 @@ A list to honor all people who found some bugs, had some great ideas or contributed directly to shinpuru. ❤️ -In total, **31** different wonderful people contributed a sum of **73** issues and **14** pull requests (with 631 added and 81 deleted lines of code in 46 different files)! 🎉 +In total, **31** different wonderful people contributed a sum of **73** issues and **15** pull requests (with 635 added and 85 deleted lines of code in 47 different files)! 🎉 | GitHub | Issues | PRs | Points* | |--------|--------|-----|---------| @@ -16,6 +16,7 @@ In total, **31** different wonderful people contributed a sum of **73** issues a | [cloudybyte](https://github.com/cloudybyte) | [#179](https://github.com/zekroTJA/shinpuru/issues/179), [#195](https://github.com/zekroTJA/shinpuru/issues/195), [#219](https://github.com/zekroTJA/shinpuru/issues/219), [#241](https://github.com/zekroTJA/shinpuru/issues/241) | | `4` | | [luxtracon](https://github.com/luxtracon) | [#248](https://github.com/zekroTJA/shinpuru/issues/248) | [#228](https://github.com/zekroTJA/shinpuru/pull/228) | `4` | | [LiebesWavezLP](https://github.com/LiebesWavezLP) | [#325](https://github.com/zekroTJA/shinpuru/issues/325), [#328](https://github.com/zekroTJA/shinpuru/issues/328), [#340](https://github.com/zekroTJA/shinpuru/issues/340), [#343](https://github.com/zekroTJA/shinpuru/issues/343) | | `4` | +| [zordem](https://github.com/zordem) | [#346](https://github.com/zekroTJA/shinpuru/issues/346) | [#351](https://github.com/zekroTJA/shinpuru/pull/351) | `4` | | [Eli-Dev](https://github.com/Eli-Dev) | [#45](https://github.com/zekroTJA/shinpuru/issues/45), [#47](https://github.com/zekroTJA/shinpuru/issues/47), [#49](https://github.com/zekroTJA/shinpuru/issues/49) | | `3` | | [InterXellar](https://github.com/InterXellar) | [#70](https://github.com/zekroTJA/shinpuru/issues/70), [#73](https://github.com/zekroTJA/shinpuru/issues/73), [#74](https://github.com/zekroTJA/shinpuru/issues/74) | | `3` | | [awsdcrafting](https://github.com/awsdcrafting) | | [#294](https://github.com/zekroTJA/shinpuru/pull/294) | `3` | @@ -35,7 +36,6 @@ In total, **31** different wonderful people contributed a sum of **73** issues a | [SCDerox](https://github.com/SCDerox) | [#280](https://github.com/zekroTJA/shinpuru/issues/280) | | `1` | | [MeerBiene](https://github.com/MeerBiene) | [#308](https://github.com/zekroTJA/shinpuru/issues/308) | | `1` | | [shiipou](https://github.com/shiipou) | [#317](https://github.com/zekroTJA/shinpuru/issues/317) | | `1` | -| [zordem](https://github.com/zordem) | [#346](https://github.com/zekroTJA/shinpuru/issues/346) | | `1` | | [kindh0623](https://github.com/kindh0623) | [#348](https://github.com/zekroTJA/shinpuru/issues/348) | | `1` | From 5bf3dc45b8b8c8779204f5455520f1b627f2c7da Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 13 Feb 2022 00:38:40 +0000 Subject: [PATCH 05/17] update bughunters --- bughunters.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bughunters.md b/bughunters.md index f67d769d..13c08db4 100644 --- a/bughunters.md +++ b/bughunters.md @@ -2,7 +2,7 @@ A list to honor all people who found some bugs, had some great ideas or contributed directly to shinpuru. ❤️ -In total, **31** different wonderful people contributed a sum of **73** issues and **15** pull requests (with 635 added and 85 deleted lines of code in 47 different files)! 🎉 +In total, **31** different wonderful people contributed a sum of **75** issues and **16** pull requests (with 637 added and 87 deleted lines of code in 48 different files)! 🎉 | GitHub | Issues | PRs | Points* | |--------|--------|-----|---------| @@ -12,11 +12,12 @@ In total, **31** different wonderful people contributed a sum of **73** issues a | [Ron31](https://github.com/Ron31) | [#197](https://github.com/zekroTJA/shinpuru/issues/197), [#224](https://github.com/zekroTJA/shinpuru/issues/224) | [#32](https://github.com/zekroTJA/shinpuru/pull/32), [#181](https://github.com/zekroTJA/shinpuru/pull/181) | `8` | | [ShowMeYourSkil](https://github.com/ShowMeYourSkil) | [#140](https://github.com/zekroTJA/shinpuru/issues/140), [#171](https://github.com/zekroTJA/shinpuru/issues/171), [#191](https://github.com/zekroTJA/shinpuru/issues/191), [#192](https://github.com/zekroTJA/shinpuru/issues/192), [#211](https://github.com/zekroTJA/shinpuru/issues/211), [#240](https://github.com/zekroTJA/shinpuru/issues/240) | | `6` | | [Skillkiller](https://github.com/Skillkiller) | [#180](https://github.com/zekroTJA/shinpuru/issues/180), [#270](https://github.com/zekroTJA/shinpuru/issues/270), [#284](https://github.com/zekroTJA/shinpuru/issues/284) | [#79](https://github.com/zekroTJA/shinpuru/pull/79) | `6` | +| [zordem](https://github.com/zordem) | [#346](https://github.com/zekroTJA/shinpuru/issues/346), [#353](https://github.com/zekroTJA/shinpuru/issues/353), [#354](https://github.com/zekroTJA/shinpuru/issues/354) | [#351](https://github.com/zekroTJA/shinpuru/pull/351) | `6` | | [Not-Nik](https://github.com/Not-Nik) | [#53](https://github.com/zekroTJA/shinpuru/issues/53) | [#56](https://github.com/zekroTJA/shinpuru/pull/56) | `4` | | [cloudybyte](https://github.com/cloudybyte) | [#179](https://github.com/zekroTJA/shinpuru/issues/179), [#195](https://github.com/zekroTJA/shinpuru/issues/195), [#219](https://github.com/zekroTJA/shinpuru/issues/219), [#241](https://github.com/zekroTJA/shinpuru/issues/241) | | `4` | | [luxtracon](https://github.com/luxtracon) | [#248](https://github.com/zekroTJA/shinpuru/issues/248) | [#228](https://github.com/zekroTJA/shinpuru/pull/228) | `4` | +| [SCDerox](https://github.com/SCDerox) | [#280](https://github.com/zekroTJA/shinpuru/issues/280) | [#352](https://github.com/zekroTJA/shinpuru/pull/352) | `4` | | [LiebesWavezLP](https://github.com/LiebesWavezLP) | [#325](https://github.com/zekroTJA/shinpuru/issues/325), [#328](https://github.com/zekroTJA/shinpuru/issues/328), [#340](https://github.com/zekroTJA/shinpuru/issues/340), [#343](https://github.com/zekroTJA/shinpuru/issues/343) | | `4` | -| [zordem](https://github.com/zordem) | [#346](https://github.com/zekroTJA/shinpuru/issues/346) | [#351](https://github.com/zekroTJA/shinpuru/pull/351) | `4` | | [Eli-Dev](https://github.com/Eli-Dev) | [#45](https://github.com/zekroTJA/shinpuru/issues/45), [#47](https://github.com/zekroTJA/shinpuru/issues/47), [#49](https://github.com/zekroTJA/shinpuru/issues/49) | | `3` | | [InterXellar](https://github.com/InterXellar) | [#70](https://github.com/zekroTJA/shinpuru/issues/70), [#73](https://github.com/zekroTJA/shinpuru/issues/73), [#74](https://github.com/zekroTJA/shinpuru/issues/74) | | `3` | | [awsdcrafting](https://github.com/awsdcrafting) | | [#294](https://github.com/zekroTJA/shinpuru/pull/294) | `3` | @@ -33,7 +34,6 @@ In total, **31** different wonderful people contributed a sum of **73** issues a | [maxcutie](https://github.com/maxcutie) | [#239](https://github.com/zekroTJA/shinpuru/issues/239) | | `1` | | [PushkarOP](https://github.com/PushkarOP) | [#269](https://github.com/zekroTJA/shinpuru/issues/269) | | `1` | | [enkeyz](https://github.com/enkeyz) | [#279](https://github.com/zekroTJA/shinpuru/issues/279) | | `1` | -| [SCDerox](https://github.com/SCDerox) | [#280](https://github.com/zekroTJA/shinpuru/issues/280) | | `1` | | [MeerBiene](https://github.com/MeerBiene) | [#308](https://github.com/zekroTJA/shinpuru/issues/308) | | `1` | | [shiipou](https://github.com/shiipou) | [#317](https://github.com/zekroTJA/shinpuru/issues/317) | | `1` | | [kindh0623](https://github.com/kindh0623) | [#348](https://github.com/zekroTJA/shinpuru/issues/348) | | `1` | From 96036d7c2df1b955eca42601d86b2bfdd89d59bc Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 13 Feb 2022 10:13:09 +0100 Subject: [PATCH 06/17] Fixed some more typos --- docs/restapi/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/restapi/README.md b/docs/restapi/README.md index ae9e93b1..8db2859a 100644 --- a/docs/restapi/README.md +++ b/docs/restapi/README.md @@ -36,11 +36,11 @@ There are two ways around this. 1. Use pointers for everything. 2. Specify that **all** values are defined as "set". -The first solution was not suitable in my opinion, because it would reqire a lot code around `nil` and proper value checking of each model property, which would also introduce a lot new fault sources. Also, because shinpuru's API utilizes a lot of the original models of [discordgo](https://github.com/bwmarrin/discordgo), this would require a lot of model wrapping and double definitions. +The first solution was not suitable in my opinion, because it would require a lot code around `nil` and proper value checking of each model property, which would also introduce a lot new fault sources. Also, because shinpuru's API utilizes a lot of the original models of [discordgo](https://github.com/bwmarrin/discordgo), this would require a lot of model wrapping and double definitions. So, I went for the second solution*. -Every property is specified as "set" by the API on update. That means, if you pass `null` as value of a string, that means the valaue of the property will be updated to `""`, which is the default zero value of a string. Long story short, even if you want to update only single properties of a model, you must pass the whole model on update to ensure consistency, even if this means that you need to get the current values before you can update them. +Every property is specified as "set" by the API on update. That means, if you pass `null` as value of a string, that means the value of the property will be updated to `""`, which is the default zero value of a string. Long story short, even if you want to update only single properties of a model, you must pass the whole model on update to ensure consistency, even if this means that you need to get the current values before you can update them. Let's take the [`/settings/presence` endpoint](), for example. We want to update the `game` value. From 86259a2d95e65b615654f3c0e8ae73c621348110 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 13 Feb 2022 10:15:32 +0100 Subject: [PATCH 07/17] Fixed typo --- docs/restapi/v1/restapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/restapi/v1/restapi.md b/docs/restapi/v1/restapi.md index 33922c81..40fcf2e5 100644 --- a/docs/restapi/v1/restapi.md +++ b/docs/restapi/v1/restapi.md @@ -48,7 +48,7 @@ Logout ##### Description -Reovkes the currently used access token and clears the refresh token. +Revokes the currently used access token and clears the refresh token. ##### Responses From 165a779aaf6a5a7443570636becfc2415dbf512e Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 13 Feb 2022 10:29:26 +0100 Subject: [PATCH 08/17] fixed even more typos --- CONTRIBUTING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4be0211..fca1c271 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,7 @@ shinpuru mainly uses MySQL/MariaDB as database. You *can* also use SQLite3 for d - Linux: https://opensource.com/article/20/10/mariadb-mysql-linux - Docker: https://hub.docker.com/_/mariadb/ -Redis is used as database cache. The [`RedisMiddleware`](https://github.com/zekroTJA/shinpuru/blob/master/internal/services/database/redis/redis.go) generaly inherits functionalities from the specified database middleware instance and only overwrites using the specified functions. The database cache always keeps the cache as well as the database hot and always first tries to get objects from cache and, if not available there, from database. +Redis is used as database cache. The [`RedisMiddleware`](https://github.com/zekroTJA/shinpuru/blob/master/internal/services/database/redis/redis.go) generally inherits functionalities from the specified database middleware instance and only overwrites using the specified functions. The database cache always keeps the cache as well as the database hot and always first tries to get objects from cache and, if not available there, from database. ![](https://i.imgur.com/TgkuhUY.png) @@ -65,7 +65,7 @@ If you want to add API endpoints, just add the endpoints to one of the controlle Also, fiber works a lot with middlewares, which can be chained anywhere into the fiber route chain. In shinpuru's implementation, there are three main types of middlewares. 1. The high level middlewares like the rate limiter, CORS or file system middleware, which are set before all incomming requests. -2. Controller specific middlewares which are defined in the router. Mainly, this is used for the authorizeation middleware, which checks for auth tokens in the requests. This middleware is required by some controllers and not required for others. +2. Controller specific middlewares which are defined in the router. Mainly, this is used for the authorization middleware, which checks for auth tokens in the requests. This middleware is required by some controllers and not required for others. 3. Endpoint specific middlewares which are defined for specific endpoints only. Mainly, this is used for the permission middleware which checks for required user permissions to execute specific endpoints. Here you can see a simple overview over the routing structure of the shinpuru webserver. @@ -98,7 +98,7 @@ As you can see, all service identifiers are registered in the [`internal/util/st After building the `diBuilder`, you will have a `di.Container` to work with where you can get any service registered. Because all services are registered in the `App` scope, once they are initialized, all requests are getting the same instance of the service. This makes service development very easy, because every service is getting passed the same service container and every service can grab the instance of any other registered service instance. -When you want to use a service, just take it from the passed service conatiner by the specified identifier. Let's take a look at the [`starboard` listener](https://github.com/zekroTJA/shinpuru/blob/master/internal/listeners/starboard.go) , for example: +When you want to use a service, just take it from the passed service container by the specified identifier. Let's take a look at the [`starboard` listener](https://github.com/zekroTJA/shinpuru/blob/master/internal/listeners/starboard.go), for example: ```go func NewListenerStarboard(container di.Container) *ListenerStarboard { @@ -134,7 +134,7 @@ This package is using a [crontab styled syntax](https://pkg.go.dev/github.com/ro The shinpuru web frontend is a compiled [**Angular**](https://angular.io) SPA, which is directly hosted form the shinpuru web server. The source files are located at [`/web`](https://github.com/zekroTJA/shinpuru/blob/master/web) Stylesheets are written in [**SCSS**](https://sass-lang.com/documentation/syntax) because SCSS has huge advantages to default CSS like nesting, mixins and variables, which are widely used in stylesheets. -The Angular web app is built like a typical Angular application with reusable components, routes, services and pipes. The communication with the REST API is handled by the [`APIService`](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/api.service.ts). API models are specified in the [`api.models.ts`](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/api.models.ts) file. Also, the API stores some objects like member information in a [`CacheBucket`](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/api.cache.ts) for short-time caching them on the client side to reduce the load on the REST API. Also, an [interceptor](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/auth.interceptor.ts) is chained before the API service which adds the collected `accessToken` to each reqeust. If the `accessToken` is not existent, expired or invalid, the `accessToken` will be collected using the `refreshToken` set as cookie. The access token is then stored and the request is retried with the now existent access token. +The Angular web app is built like a typical Angular application with reusable components, routes, services and pipes. The communication with the REST API is handled by the [`APIService`](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/api.service.ts). API models are specified in the [`api.models.ts`](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/api.models.ts) file. Also, the API stores some objects like member information in a [`CacheBucket`](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/api.cache.ts) for short-time caching them on the client side to reduce the load on the REST API. Also, an [interceptor](https://github.com/zekroTJA/shinpuru/blob/master/web/src/app/api/auth.interceptor.ts) is chained before the API service which adds the collected `accessToken` to each request. If the `accessToken` is not existent, expired or invalid, the `accessToken` will be collected using the `refreshToken` set as cookie. The access token is then stored and the request is retried with the now existent access token. ## Preparing a Development Environment @@ -160,4 +160,4 @@ So, you want to contribute to shinpuru but you don't know what exactly you want ## Any Questions? -If you have any questions, please hit me on my [**Dev Discord**](https://discord.zekro.de) (`zekro#0001`) or on [**Twitter**](https://twitter.com/zekrotja). You can also simply send me an [e-mail](mailto:contact@zekro.de). 😉 \ No newline at end of file +If you have any questions, please hit me on my [**Dev Discord**](https://discord.zekro.de) (`zekro#0001`) or on [**Twitter**](https://twitter.com/zekrotja). You can also simply send me an [e-mail](mailto:contact@zekro.de). 😉 From 39b3a80f4b9bb4383a45f738ff8f8994934ded71 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 16 Feb 2022 00:36:16 +0000 Subject: [PATCH 09/17] update bughunters --- bughunters.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bughunters.md b/bughunters.md index 13c08db4..f4c22f44 100644 --- a/bughunters.md +++ b/bughunters.md @@ -2,7 +2,7 @@ A list to honor all people who found some bugs, had some great ideas or contributed directly to shinpuru. ❤️ -In total, **31** different wonderful people contributed a sum of **75** issues and **16** pull requests (with 637 added and 87 deleted lines of code in 48 different files)! 🎉 +In total, **31** different wonderful people contributed a sum of **75** issues and **17** pull requests (with 645 added and 95 deleted lines of code in 51 different files)! 🎉 | GitHub | Issues | PRs | Points* | |--------|--------|-----|---------| @@ -10,13 +10,13 @@ In total, **31** different wonderful people contributed a sum of **75** issues a | 🥈 [voxain](https://github.com/voxain) | [#52](https://github.com/zekroTJA/shinpuru/issues/52), [#61](https://github.com/zekroTJA/shinpuru/issues/61), [#67](https://github.com/zekroTJA/shinpuru/issues/67), [#147](https://github.com/zekroTJA/shinpuru/issues/147), [#148](https://github.com/zekroTJA/shinpuru/issues/148), [#150](https://github.com/zekroTJA/shinpuru/issues/150), [#153](https://github.com/zekroTJA/shinpuru/issues/153), [#159](https://github.com/zekroTJA/shinpuru/issues/159), [#163](https://github.com/zekroTJA/shinpuru/issues/163), [#165](https://github.com/zekroTJA/shinpuru/issues/165), [#183](https://github.com/zekroTJA/shinpuru/issues/183), [#187](https://github.com/zekroTJA/shinpuru/issues/187), [#203](https://github.com/zekroTJA/shinpuru/issues/203), [#210](https://github.com/zekroTJA/shinpuru/issues/210), [#249](https://github.com/zekroTJA/shinpuru/issues/249), [#250](https://github.com/zekroTJA/shinpuru/issues/250) | | `16` | | 🥉 [error2507](https://github.com/error2507) | [#28](https://github.com/zekroTJA/shinpuru/issues/28), [#29](https://github.com/zekroTJA/shinpuru/issues/29), [#55](https://github.com/zekroTJA/shinpuru/issues/55) | [#1](https://github.com/zekroTJA/shinpuru/pull/1), [#2](https://github.com/zekroTJA/shinpuru/pull/2) | `9` | | [Ron31](https://github.com/Ron31) | [#197](https://github.com/zekroTJA/shinpuru/issues/197), [#224](https://github.com/zekroTJA/shinpuru/issues/224) | [#32](https://github.com/zekroTJA/shinpuru/pull/32), [#181](https://github.com/zekroTJA/shinpuru/pull/181) | `8` | +| [SCDerox](https://github.com/SCDerox) | [#280](https://github.com/zekroTJA/shinpuru/issues/280) | [#352](https://github.com/zekroTJA/shinpuru/pull/352), [#355](https://github.com/zekroTJA/shinpuru/pull/355) | `7` | | [ShowMeYourSkil](https://github.com/ShowMeYourSkil) | [#140](https://github.com/zekroTJA/shinpuru/issues/140), [#171](https://github.com/zekroTJA/shinpuru/issues/171), [#191](https://github.com/zekroTJA/shinpuru/issues/191), [#192](https://github.com/zekroTJA/shinpuru/issues/192), [#211](https://github.com/zekroTJA/shinpuru/issues/211), [#240](https://github.com/zekroTJA/shinpuru/issues/240) | | `6` | | [Skillkiller](https://github.com/Skillkiller) | [#180](https://github.com/zekroTJA/shinpuru/issues/180), [#270](https://github.com/zekroTJA/shinpuru/issues/270), [#284](https://github.com/zekroTJA/shinpuru/issues/284) | [#79](https://github.com/zekroTJA/shinpuru/pull/79) | `6` | | [zordem](https://github.com/zordem) | [#346](https://github.com/zekroTJA/shinpuru/issues/346), [#353](https://github.com/zekroTJA/shinpuru/issues/353), [#354](https://github.com/zekroTJA/shinpuru/issues/354) | [#351](https://github.com/zekroTJA/shinpuru/pull/351) | `6` | | [Not-Nik](https://github.com/Not-Nik) | [#53](https://github.com/zekroTJA/shinpuru/issues/53) | [#56](https://github.com/zekroTJA/shinpuru/pull/56) | `4` | | [cloudybyte](https://github.com/cloudybyte) | [#179](https://github.com/zekroTJA/shinpuru/issues/179), [#195](https://github.com/zekroTJA/shinpuru/issues/195), [#219](https://github.com/zekroTJA/shinpuru/issues/219), [#241](https://github.com/zekroTJA/shinpuru/issues/241) | | `4` | | [luxtracon](https://github.com/luxtracon) | [#248](https://github.com/zekroTJA/shinpuru/issues/248) | [#228](https://github.com/zekroTJA/shinpuru/pull/228) | `4` | -| [SCDerox](https://github.com/SCDerox) | [#280](https://github.com/zekroTJA/shinpuru/issues/280) | [#352](https://github.com/zekroTJA/shinpuru/pull/352) | `4` | | [LiebesWavezLP](https://github.com/LiebesWavezLP) | [#325](https://github.com/zekroTJA/shinpuru/issues/325), [#328](https://github.com/zekroTJA/shinpuru/issues/328), [#340](https://github.com/zekroTJA/shinpuru/issues/340), [#343](https://github.com/zekroTJA/shinpuru/issues/343) | | `4` | | [Eli-Dev](https://github.com/Eli-Dev) | [#45](https://github.com/zekroTJA/shinpuru/issues/45), [#47](https://github.com/zekroTJA/shinpuru/issues/47), [#49](https://github.com/zekroTJA/shinpuru/issues/49) | | `3` | | [InterXellar](https://github.com/InterXellar) | [#70](https://github.com/zekroTJA/shinpuru/issues/70), [#73](https://github.com/zekroTJA/shinpuru/issues/73), [#74](https://github.com/zekroTJA/shinpuru/issues/74) | | `3` | From 9ebde786bf7047790dc58be4c5e6381ef4bbb067 Mon Sep 17 00:00:00 2001 From: Ringo Hoffmann Date: Fri, 18 Feb 2022 15:45:30 +0100 Subject: [PATCH 10/17] update changelog --- CHANGELOG.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c50bd3..c60b857a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,29 @@ [VERSION] -> **Attention** -> This is a hotfix patch. If you want to see the changelog for release 1.30.0, please look [**here**](https://github.com/zekroTJA/shinpuru/releases/tag/1.30.0). + + +# New Login Method + +On the main page, when clicking on `Login`, you can now choose between logging in to the web interface via OAauth2 using your Discord account or log in via sending the code, which is displayed in the web interface, to shinpuru via DM. + +![](https://user-images.githubusercontent.com/16734205/154697491-b0aa34d3-ff79-40ee-9b49-ec77cfc23cee.gif) + +# Deployment + +The generated frontend files are now directly embedded into the binary of shinpuru. Therefore, downloading and providing the frontend files in the same directory of the binary is no more necessary. # Bug Fixes -- Delete command message of `/birthday set` when `show-age` is set to `False`. +- The birtdhay command will now only send command responses to the user who invoked the command. [#354] +- The embed builder now only shows available text channels where the logged in user has read and write permissions. [#353] +- The embed preview now shows a placeholder title and description when empty. [#353] + +# Acknowledgements + +Big thanks to the following people who contributed to this release. + +- @zordem # Docker From 1d3fa5c826e8e002612de5541c3916fd768a5ee5 Mon Sep 17 00:00:00 2001 From: TomRomeo Date: Sun, 20 Feb 2022 16:54:17 +0100 Subject: [PATCH 11/17] add slashcommands for auto voicechannels --- internal/commands/cmdautovc.go | 146 ++++++++++++++++++++++ internal/slashcommands/autovc.go | 201 +++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) create mode 100644 internal/commands/cmdautovc.go create mode 100644 internal/slashcommands/autovc.go diff --git a/internal/commands/cmdautovc.go b/internal/commands/cmdautovc.go new file mode 100644 index 00000000..e56aa57d --- /dev/null +++ b/internal/commands/cmdautovc.go @@ -0,0 +1,146 @@ +package commands + +import ( + "fmt" + "strings" + "time" + + "github.com/bwmarrin/discordgo" + "github.com/zekroTJA/shinpuru/internal/services/database" + "github.com/zekroTJA/shinpuru/internal/util" + "github.com/zekroTJA/shinpuru/internal/util/static" + "github.com/zekroTJA/shinpuru/pkg/acceptmsg" + "github.com/zekroTJA/shinpuru/pkg/fetch" + "github.com/zekroTJA/shinpuru/pkg/stringutil" + "github.com/zekroTJA/shireikan" + "github.com/zekrotja/dgrs" +) + +type CmdAutovc struct { +} + +func (c *CmdAutovc) GetInvokes() []string { + return []string{"autovoice", "autovc", "avc", "avcs"} +} + +func (c *CmdAutovc) GetDescription() string { + return "Set the auto voicechannel for the current guild." +} + +func (c *CmdAutovc) GetHelp() string { + return "`autovc` - display currently set auto voicechannel(s)\n" + + "`autovc ( ( (...)))` - set an auto voicechannel for the current guild\n" + + "`autovc reset` - disable auto voicechannels" +} + +func (c *CmdAutovc) GetGroup() string { + return shireikan.GroupGuildConfig +} + +func (c *CmdAutovc) GetDomainName() string { + return "sp.guild.config.autovc" +} + +func (c *CmdAutovc) GetSubPermissionRules() []shireikan.SubPermission { + return nil +} + +func (c *CmdAutovc) IsExecutableInDMChannels() bool { + return false +} + +func (c *CmdAutovc) Exec(ctx shireikan.Context) error { + db, _ := ctx.GetObject(static.DiDatabase).(database.Database) + st, _ := ctx.GetObject(static.DiState).(*dgrs.State) + + if len(ctx.GetArgs()) == 0 { + return c.list(ctx, db, st) + } + if ctx.GetArgs().Get(0).AsString() == "reset" { + return c.reset(ctx, db) + } + return c.set(ctx, db, st) +} + +func (c *CmdAutovc) list(ctx shireikan.Context, db database.Database, st *dgrs.State) (err error) { + autoVCIDs, err := db.GetGuildAutoVC(ctx.GetGuild().ID) + if err != nil && !database.IsErrDatabaseNotFound(err) { + return + } + + if len(autoVCIDs) == 0 { + return util.SendEmbed(ctx.GetSession(), ctx.GetChannel().ID, + "No auto voicechannels are set.", "", 0).DeleteAfter(10 * time.Second).Error() + } + + guildChannels, err := st.Channels(ctx.GetGuild().ID, true) + if err != nil { + return err + } + guildChannelIDs := make([]string, len(guildChannels)) + for i, channel := range guildChannels { + guildChannelIDs[i] = channel.ID + } + + if nc := stringutil.NotContained(autoVCIDs, guildChannelIDs); len(nc) > 0 { + autoVCIDs = stringutil.Contained(autoVCIDs, guildChannelIDs) + am, err := acceptmsg.New(). + WithSession(ctx.GetSession()). + DeleteAfterAnswer(). + LockOnUser(ctx.GetUser().ID). + WithContent(fmt.Sprintf( + "%d %s are not existent anymore. "+ + "Do you want to remove them now from the list of auto voicechannels?", + len(nc), util.Pluralize(len(nc), "autovoicechannel"))). + DoOnAccept(func(_ *discordgo.Message) error { + return db.SetGuildAutoVC(ctx.GetGuild().ID, autoVCIDs) + }). + Send(ctx.GetChannel().ID) + if err != nil { + return err + } + if err = am.Error(); err != nil { + return err + } + } + + var vcNames strings.Builder + vcNames.WriteString("Following auto voicechannel(s) are set:\n") + for _, vcid := range autoVCIDs { + vcNames.WriteString(fmt.Sprintf(" - <@&%s> (%s)\n", vcid, vcid)) + } + + return util.SendEmbed(ctx.GetSession(), ctx.GetChannel().ID, + vcNames.String(), "", 0).DeleteAfter(12 * time.Second).Error() +} + +func (c *CmdAutovc) set(ctx shireikan.Context, db database.Database, st *dgrs.State) (err error) { + autoVCIDs := make([]string, 0, len(ctx.GetArgs())) + + for _, arg := range ctx.GetArgs() { + if len(arg) == 0 { + continue + } + vc, err := fetch.FetchChannel(fetch.WrapDrgs(st), ctx.GetGuild().ID, arg) + if err != nil { + return err + } + autoVCIDs = append(autoVCIDs, vc.ID) + } + + if err = db.SetGuildAutoVC(ctx.GetGuild().ID, autoVCIDs); err != nil { + return err + } + + return util.SendEmbed(ctx.GetSession(), ctx.GetChannel().ID, + "Auto voicechannels set.", "", 0).DeleteAfter(5 * time.Second).Error() +} + +func (c *CmdAutovc) reset(ctx shireikan.Context, db database.Database) (err error) { + if err = db.SetGuildAutoVC(ctx.GetGuild().ID, []string{}); err != nil { + return err + } + + return util.SendEmbed(ctx.GetSession(), ctx.GetChannel().ID, + "Auto voicechannels reseted.", "", 0).DeleteAfter(5 * time.Second).Error() +} diff --git a/internal/slashcommands/autovc.go b/internal/slashcommands/autovc.go new file mode 100644 index 00000000..fe29bd2a --- /dev/null +++ b/internal/slashcommands/autovc.go @@ -0,0 +1,201 @@ +package slashcommands + +import ( + "fmt" + "strings" + + "github.com/bwmarrin/discordgo" + "github.com/zekroTJA/shinpuru/internal/services/database" + "github.com/zekroTJA/shinpuru/internal/services/permissions" + "github.com/zekroTJA/shinpuru/internal/util/static" + "github.com/zekroTJA/shinpuru/pkg/stringutil" + "github.com/zekrotja/ken" +) + +type Autovc struct{} + +var ( + _ ken.SlashCommand = (*Autovc)(nil) + _ permissions.PermCommand = (*Autovc)(nil) +) + +func (c *Autovc) Name() string { + return "autovc" +} + +func (c *Autovc) Description() string { + return "Manage guild auto voicechannels." +} + +func (c *Autovc) Version() string { + return "1.0.0" +} + +func (c *Autovc) Type() discordgo.ApplicationCommandType { + return discordgo.ChatApplicationCommand +} + +func (c *Autovc) Options() []*discordgo.ApplicationCommandOption { + return []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionSubCommand, + Name: "show", + Description: "Display the currently set auto voicechannels.", + }, + { + Type: discordgo.ApplicationCommandOptionSubCommand, + Name: "add", + Description: "Add an auto voicechannel.", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionChannel, + Name: "voicechannel", + ChannelTypes: []discordgo.ChannelType{discordgo.ChannelTypeGuildVoice}, + Description: "The voicechannel to be set.", + Required: true, + }, + }, + }, + { + Type: discordgo.ApplicationCommandOptionSubCommand, + Name: "remove", + Description: "Remove an auto voicechannel.", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionChannel, + ChannelTypes: []discordgo.ChannelType{discordgo.ChannelTypeGuildVoice}, + Name: "voicechannel", + Description: "The voicechannel to be set.", + Required: true, + }, + }, + }, + { + Type: discordgo.ApplicationCommandOptionSubCommand, + Name: "purge", + Description: "Unset all auto voicechannels.", + }, + } +} + +func (c *Autovc) Domain() string { + return "sp.guild.config.autovc" +} + +func (c *Autovc) SubDomains() []permissions.SubPermission { + return nil +} + +func (c *Autovc) Run(ctx *ken.Ctx) (err error) { + if err = ctx.Defer(); err != nil { + return + } + + err = ctx.HandleSubCommands( + ken.SubCommandHandler{"show", c.show}, + ken.SubCommandHandler{"add", c.add}, + ken.SubCommandHandler{"remove", c.remove}, + ken.SubCommandHandler{"purge", c.purge}, + ) + + return +} + +func (c *Autovc) show(ctx *ken.SubCommandCtx) (err error) { + db := ctx.Get(static.DiDatabase).(database.Database) + + autovcs, err := db.GetGuildAutoVC(ctx.Event.GuildID) + if err != nil { + return + } + + if len(autovcs) == 0 { + err = ctx.FollowUpEmbed(&discordgo.MessageEmbed{ + Description: "Currently, no auto voicechannels are defined.", + }).Error + return + } + + var res strings.Builder + for _, id := range autovcs { + res.WriteString(fmt.Sprintf("- <#%s>\n", id)) + } + + err = ctx.FollowUpEmbed(&discordgo.MessageEmbed{ + Description: "Currently, following channels are set as auto voicechannels:\n" + res.String(), + }).Error + + return +} + +func (c *Autovc) add(ctx *ken.SubCommandCtx) (err error) { + db := ctx.Get(static.DiDatabase).(database.Database) + + vc := ctx.Options().Get(0). + ChannelValue(ctx.Ctx) + + autovcs, err := db.GetGuildAutoVC(ctx.Event.GuildID) + if err != nil { + return + } + + if stringutil.ContainsAny(vc.ID, autovcs) { + err = ctx.FollowUpError("The given voicechannel is already assigned.", "").Error + return + } + + if err = db.SetGuildAutoVC(ctx.Event.GuildID, append(autovcs, vc.ID)); err != nil { + return + } + + err = ctx.FollowUpEmbed(&discordgo.MessageEmbed{ + Color: static.ColorEmbedGreen, + Description: "Voicechannel was successfully assigned as auto voicechannel.", + }).Error + + return +} + +func (c *Autovc) remove(ctx *ken.SubCommandCtx) (err error) { + db := ctx.Get(static.DiDatabase).(database.Database) + + vc := ctx.Options().Get(0). + ChannelValue(ctx.Ctx) + + autovcs, err := db.GetGuildAutoVC(ctx.Event.GuildID) + if err != nil { + return + } + + if !stringutil.ContainsAny(vc.ID, autovcs) { + err = ctx.FollowUpError("The given voicechannel is not assigned as auto voicechannel.", "").Error + return + } + + autovcs = stringutil.Splice(autovcs, stringutil.IndexOf(vc.ID, autovcs)) + if err = db.SetGuildAutoVC(ctx.Event.GuildID, autovcs); err != nil { + return + } + + err = ctx.FollowUpEmbed(&discordgo.MessageEmbed{ + Color: static.ColorEmbedGreen, + Description: "Channel was successfully removed as autochannel.", + }).Error + + return +} + +func (c *Autovc) purge(ctx *ken.SubCommandCtx) (err error) { + db := ctx.Get(static.DiDatabase).(database.Database) + + if err = db.SetGuildAutoVC(ctx.Event.GuildID, []string{}); err != nil { + return + } + + err = ctx.FollowUpEmbed(&discordgo.MessageEmbed{ + Color: static.ColorEmbedGreen, + Description: "All auto voicechannels were successfully removed.", + }).Error + + return +} From 481e73d7165b9c95c285f29adf8bf9a193c47514 Mon Sep 17 00:00:00 2001 From: TomRomeo Date: Sun, 20 Feb 2022 16:56:43 +0100 Subject: [PATCH 12/17] add database logic for auto voicechannels --- internal/services/database/database.go | 3 ++ .../services/database/mysql/migrations.go | 9 +++++ internal/services/database/mysql/mysql.go | 13 +++++++ internal/services/database/redis/redis.go | 36 +++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/internal/services/database/database.go b/internal/services/database/database.go index 7573720c..2ae15b2f 100644 --- a/internal/services/database/database.go +++ b/internal/services/database/database.go @@ -38,6 +38,9 @@ type Database interface { GetGuildAutoRole(guildID string) ([]string, error) SetGuildAutoRole(guildID string, autoRoleIDs []string) error + GetGuildAutoVC(guildID string) ([]string, error) + SetGuildAutoVC(guildID string, autoVCIDs []string) error + GetGuildModLog(guildID string) (string, error) SetGuildModLog(guildID, chanID string) error diff --git a/internal/services/database/mysql/migrations.go b/internal/services/database/mysql/migrations.go index e673ebc2..1ceeaf65 100644 --- a/internal/services/database/mysql/migrations.go +++ b/internal/services/database/mysql/migrations.go @@ -16,6 +16,7 @@ var migrationFuncs = []migrationFunc{ migration_8, migration_9, migration_10, + migration_11, } // VERSION 0: @@ -119,3 +120,11 @@ func migration_10(m *sql.Tx) (err error) { "guilds", "`birthdaychanID` text NOT NULL DEFAULT ''") return } + +// VERSION 11: +// - add property `autovc` to `guilds` +func migration_11(m *sql.Tx) (err error) { + err = createTableColumnIfNotExists(m, + "guilds", "`autovc` text NOT NULL DEFAULT ''") + return +} diff --git a/internal/services/database/mysql/mysql.go b/internal/services/database/mysql/mysql.go index efcb95fb..8848bbba 100644 --- a/internal/services/database/mysql/mysql.go +++ b/internal/services/database/mysql/mysql.go @@ -95,6 +95,7 @@ func (m *MysqlMiddleware) setup() (err error) { "`guildID` varchar(25) NOT NULL," + "`prefix` text NOT NULL DEFAULT ''," + "`autorole` text NOT NULL DEFAULT ''," + + "`autovc` text NOT NULL DEFAULT ''," + "`modlogchanID` text NOT NULL DEFAULT ''," + "`voicelogchanID` text NOT NULL DEFAULT ''," + "`notifyRoleID` text NOT NULL DEFAULT ''," + @@ -515,6 +516,18 @@ func (m *MysqlMiddleware) SetGuildAutoRole(guildID string, autoRoleIDs []string) return m.setGuildSetting(guildID, "autorole", strings.Join(autoRoleIDs, ";")) } +func (m *MysqlMiddleware) GetGuildAutoVC(guildID string) ([]string, error) { + val, err := m.getGuildSetting(guildID, "autovc") + if val == "" { + return []string{}, err + } + return strings.Split(val, ";"), err +} + +func (m *MysqlMiddleware) SetGuildAutoVC(guildID string, autoVCIDs []string) error { + return m.setGuildSetting(guildID, "autovc", strings.Join(autoVCIDs, ";")) +} + func (m *MysqlMiddleware) GetGuildModLog(guildID string) (string, error) { val, err := m.getGuildSetting(guildID, "modlogchanID") return val, err diff --git a/internal/services/database/redis/redis.go b/internal/services/database/redis/redis.go index 5af75118..5ae6c437 100644 --- a/internal/services/database/redis/redis.go +++ b/internal/services/database/redis/redis.go @@ -17,6 +17,7 @@ const ( keyGuildPrefix = "GUILD:PREFIX" keyGuildAutoRole = "GUILD:AUTOROLE" + keyGuildAutoVC = "GUILD:AUTOVC" keyGuildModLog = "GUILD:MODLOG" keyGuildVoiceLog = "GUILD:VOICELOG" keyGuildNotifyRole = "GUILD:NOTROLE" @@ -142,6 +143,41 @@ func (r *RedisMiddleware) SetGuildAutoRole(guildID string, autoRoleIDs []string) return r.Database.SetGuildAutoRole(guildID, autoRoleIDs) } +func (r *RedisMiddleware) GetGuildAutoVC(guildID string) ([]string, error) { + var key = fmt.Sprintf("%s:%s", keyGuildAutoVC, guildID) + + valC, err := r.client.Get(context.Background(), key).Result() + val := strings.Split(valC, ";") + if err == redis.Nil { + val, err = r.Database.GetGuildAutoVC(guildID) + if err != nil { + return nil, err + } + + err = r.client.Set(context.Background(), key, strings.Join(val, ";"), 0).Err() + return val, err + } + if err != nil { + return nil, err + } + + if valC == "" { + return []string{}, nil + } + + return val, nil +} + +func (r *RedisMiddleware) SetGuildAutoVC(guildID string, autoVCIDs []string) error { + var key = fmt.Sprintf("%s:%s", keyGuildAutoVC, guildID) + + if err := r.client.Set(context.Background(), key, strings.Join(autoVCIDs, ";"), 0).Err(); err != nil { + return err + } + + return r.Database.SetGuildAutoVC(guildID, autoVCIDs) +} + func (r *RedisMiddleware) GetGuildModLog(guildID string) (string, error) { var key = fmt.Sprintf("%s:%s", keyGuildModLog, guildID) return Get(r, key, func() (string, error) { From d6eaa596c47fd0f836d62ed2d520181fb99d1d17 Mon Sep 17 00:00:00 2001 From: TomRomeo Date: Mon, 21 Feb 2022 10:05:20 +0100 Subject: [PATCH 13/17] add listeners for auto voicechannels --- internal/inits/botsession.go | 1 + internal/listeners/autovc.go | 114 +++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 internal/listeners/autovc.go diff --git a/internal/inits/botsession.go b/internal/inits/botsession.go index 151bb4f4..1d25ffc4 100644 --- a/internal/inits/botsession.go +++ b/internal/inits/botsession.go @@ -93,6 +93,7 @@ func InitDiscordBotSession(container di.Container) (release func()) { session.AddHandler(listeners.NewListenerVote(container).Handler) session.AddHandler(listeners.NewListenerChannelCreate(container).Handler) session.AddHandler(listeners.NewListenerVoiceUpdate(container).Handler) + session.AddHandler(listeners.NewListenerAutoVoice(container).Handler) session.AddHandler(listeners.NewListenerKarma(container).Handler) session.AddHandler(listeners.NewListenerAntiraid(container).HandlerMemberAdd) session.AddHandler(listeners.NewListenerBotMention(container).Listener) diff --git a/internal/listeners/autovc.go b/internal/listeners/autovc.go new file mode 100644 index 00000000..6424149b --- /dev/null +++ b/internal/listeners/autovc.go @@ -0,0 +1,114 @@ +package listeners + +import ( + "github.com/bwmarrin/discordgo" + "github.com/sarulabs/di/v2" + "github.com/zekroTJA/shinpuru/internal/services/database" + "github.com/zekroTJA/shinpuru/internal/services/permissions" + "github.com/zekroTJA/shinpuru/internal/util/static" + "github.com/zekrotja/dgrs" + "log" + "strings" +) + +var autovcCache = map[string]string{} +var voiceStateCache = map[string]*discordgo.VoiceState{} + +type ListenerAutoVoice struct { + db database.Database + st *dgrs.State + pmw *permissions.Permissions +} + +func NewListenerAutoVoice(container di.Container) *ListenerAutoVoice { + return &ListenerAutoVoice{ + db: container.Get(static.DiDatabase).(database.Database), + st: container.Get(static.DiState).(*dgrs.State), + pmw: container.Get(static.DiPermissions).(*permissions.Permissions), + } +} + +func (l *ListenerAutoVoice) Handler(s *discordgo.Session, e *discordgo.VoiceStateUpdate) { + + allowed, _, err := l.pmw.CheckPermissions(s, e.GuildID, e.UserID, "sp.chat.autochannel") + if err != nil || !allowed { + return + } + vsOld, _ := voiceStateCache[e.UserID] + vsNew := e.VoiceState + + voiceStateCache[e.UserID] = vsNew + + ids, err := l.db.GetGuildAutoVC(e.GuildID) + if err != nil { + return + } + idString := strings.Join(ids, ";") + + if vsOld == nil || (vsOld != nil && vsOld.ChannelID == "") { + + if !strings.Contains(idString, vsNew.ChannelID) { + return + } + + if err := l.createAutoVC(s, e.UserID, e.GuildID); err != nil { + return + } + + } else if vsOld != nil && vsNew.ChannelID != "" && vsOld.ChannelID != vsNew.ChannelID { + + // we don't want to delete the channel, if the user get's moved to their auto voicechannel + if vsNew.ChannelID == autovcCache[e.UserID] { + + } else if strings.Contains(idString, vsNew.ChannelID) && autovcCache[e.UserID] == "" { + if autovcCache[e.UserID] == "" { + if err := l.createAutoVC(s, e.UserID, e.GuildID); err != nil { + return + } + } else { + if err := l.deleteAutoVC(s, e.UserID); err != nil { + return + } + } + } else if autovcCache[e.UserID] != "" { + if err := l.deleteAutoVC(s, e.UserID); err != nil { + return + } + } + + } else if vsOld != nil && vsNew.ChannelID == "" { + if autovcCache[e.UserID] != "" { + if err := l.deleteAutoVC(s, e.UserID); err != nil { + return + } + } + + } +} + +func (l *ListenerAutoVoice) createAutoVC(s *discordgo.Session, userID, guildID string) error { + user, err := l.st.User(userID) + if err != nil { + return err + } + ch, err := s.GuildChannelCreate(guildID, user.Username, discordgo.ChannelTypeGuildVoice) + if err != nil { + return err + } + autovcCache[userID] = ch.ID + if err := s.GuildMemberMove(guildID, userID, &ch.ID); err != nil { + log.Println(err) + return err + } + return nil +} + +func (l *ListenerAutoVoice) deleteAutoVC(s *discordgo.Session, userID string) error { + vcID := autovcCache[userID] + _, err := s.ChannelDelete(vcID) + if err != nil { + return err + } + delete(autovcCache, userID) + return nil +} From 093d576e9bac59d3f49689c79ba34b116d176fbd Mon Sep 17 00:00:00 2001 From: TomRomeo Date: Mon, 21 Feb 2022 10:05:52 +0100 Subject: [PATCH 14/17] add commandhandler for auto voicechannels --- internal/inits/commandhandler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/inits/commandhandler.go b/internal/inits/commandhandler.go index cca11be2..4c8678e9 100644 --- a/internal/inits/commandhandler.go +++ b/internal/inits/commandhandler.go @@ -54,6 +54,7 @@ func InitCommandHandler(container di.Container) (k *ken.Ken, err error) { new(messagecommands.Quote), new(slashcommands.Autorole), + new(slashcommands.Autovc), new(slashcommands.Backup), new(slashcommands.Bug), new(slashcommands.Clear), From d96892fea5b5d2d1d7bd540dfeb9e43c37fe6388 Mon Sep 17 00:00:00 2001 From: TomRomeo Date: Tue, 22 Feb 2022 08:51:38 +0100 Subject: [PATCH 15/17] add missing call of legacy cmd handler --- internal/inits/legacycmdhandler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/inits/legacycmdhandler.go b/internal/inits/legacycmdhandler.go index d5078c8c..a8fd8e9b 100644 --- a/internal/inits/legacycmdhandler.go +++ b/internal/inits/legacycmdhandler.go @@ -75,6 +75,7 @@ func InitLegacyCommandHandler(container di.Container) shireikan.Handler { cmdHandler.RegisterCommand(&commands.CmdQuote{}) cmdHandler.RegisterCommand(&commands.CmdGame{}) cmdHandler.RegisterCommand(&commands.CmdAutorole{}) + cmdHandler.RegisterCommand(&commands.CmdAutovc{}) cmdHandler.RegisterCommand(&commands.CmdReport{}) cmdHandler.RegisterCommand(&commands.CmdModlog{}) cmdHandler.RegisterCommand(&commands.CmdKick{}) From 366d46a1e8d0051e49dac64f046d2ab5ac971657 Mon Sep 17 00:00:00 2001 From: TomRomeo Date: Tue, 22 Feb 2022 09:16:11 +0100 Subject: [PATCH 16/17] fix positioning for created auto VCs --- internal/listeners/autovc.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/internal/listeners/autovc.go b/internal/listeners/autovc.go index 6424149b..194b4fb5 100644 --- a/internal/listeners/autovc.go +++ b/internal/listeners/autovc.go @@ -7,7 +7,6 @@ import ( "github.com/zekroTJA/shinpuru/internal/services/permissions" "github.com/zekroTJA/shinpuru/internal/util/static" "github.com/zekrotja/dgrs" - "log" "strings" ) @@ -51,7 +50,7 @@ func (l *ListenerAutoVoice) Handler(s *discordgo.Session, e *discordgo.VoiceStat return } - if err := l.createAutoVC(s, e.UserID, e.GuildID); err != nil { + if err := l.createAutoVC(s, e.UserID, e.GuildID, vsNew.ChannelID); err != nil { return } @@ -62,7 +61,7 @@ func (l *ListenerAutoVoice) Handler(s *discordgo.Session, e *discordgo.VoiceStat } else if strings.Contains(idString, vsNew.ChannelID) && autovcCache[e.UserID] == "" { if autovcCache[e.UserID] == "" { - if err := l.createAutoVC(s, e.UserID, e.GuildID); err != nil { + if err := l.createAutoVC(s, e.UserID, e.GuildID, vsNew.ChannelID); err != nil { return } } else { @@ -86,7 +85,11 @@ func (l *ListenerAutoVoice) Handler(s *discordgo.Session, e *discordgo.VoiceStat } } -func (l *ListenerAutoVoice) createAutoVC(s *discordgo.Session, userID, guildID string) error { +func (l *ListenerAutoVoice) createAutoVC(s *discordgo.Session, userID, guildID, parentChannelId string) error { + parentCh, err := l.st.Channel(parentChannelId) + if err != nil { + return err + } user, err := l.st.User(userID) if err != nil { return err @@ -95,9 +98,15 @@ func (l *ListenerAutoVoice) createAutoVC(s *discordgo.Session, userID, guildID s if err != nil { return err } + ch, err = s.ChannelEditComplex(ch.ID, &discordgo.ChannelEdit{ + ParentID: parentCh.ParentID, + Position: parentCh.Position, + }) + if err != nil { + return err + } autovcCache[userID] = ch.ID if err := s.GuildMemberMove(guildID, userID, &ch.ID); err != nil { - log.Println(err) return err } return nil From fefc82261464d3332e7c301472bb5b7e47bcdb9b Mon Sep 17 00:00:00 2001 From: TomRomeo Date: Tue, 22 Feb 2022 09:58:17 +0100 Subject: [PATCH 17/17] fix singleton dependencies --- internal/listeners/autovc.go | 39 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/internal/listeners/autovc.go b/internal/listeners/autovc.go index 194b4fb5..d8225d8c 100644 --- a/internal/listeners/autovc.go +++ b/internal/listeners/autovc.go @@ -10,20 +10,21 @@ import ( "strings" ) -var autovcCache = map[string]string{} -var voiceStateCache = map[string]*discordgo.VoiceState{} - type ListenerAutoVoice struct { - db database.Database - st *dgrs.State - pmw *permissions.Permissions + db database.Database + st *dgrs.State + pmw *permissions.Permissions + autovcCache map[string]string + voiceStateCache map[string]*discordgo.VoiceState } func NewListenerAutoVoice(container di.Container) *ListenerAutoVoice { return &ListenerAutoVoice{ - db: container.Get(static.DiDatabase).(database.Database), - st: container.Get(static.DiState).(*dgrs.State), - pmw: container.Get(static.DiPermissions).(*permissions.Permissions), + db: container.Get(static.DiDatabase).(database.Database), + st: container.Get(static.DiState).(*dgrs.State), + pmw: container.Get(static.DiPermissions).(*permissions.Permissions), + autovcCache: map[string]string{}, + voiceStateCache: map[string]*discordgo.VoiceState{}, } } @@ -33,10 +34,10 @@ func (l *ListenerAutoVoice) Handler(s *discordgo.Session, e *discordgo.VoiceStat if err != nil || !allowed { return } - vsOld, _ := voiceStateCache[e.UserID] + vsOld, _ := l.voiceStateCache[e.UserID] vsNew := e.VoiceState - voiceStateCache[e.UserID] = vsNew + l.voiceStateCache[e.UserID] = vsNew ids, err := l.db.GetGuildAutoVC(e.GuildID) if err != nil { @@ -57,10 +58,10 @@ func (l *ListenerAutoVoice) Handler(s *discordgo.Session, e *discordgo.VoiceStat } else if vsOld != nil && vsNew.ChannelID != "" && vsOld.ChannelID != vsNew.ChannelID { // we don't want to delete the channel, if the user get's moved to their auto voicechannel - if vsNew.ChannelID == autovcCache[e.UserID] { + if vsNew.ChannelID == l.autovcCache[e.UserID] { - } else if strings.Contains(idString, vsNew.ChannelID) && autovcCache[e.UserID] == "" { - if autovcCache[e.UserID] == "" { + } else if strings.Contains(idString, vsNew.ChannelID) && l.autovcCache[e.UserID] == "" { + if l.autovcCache[e.UserID] == "" { if err := l.createAutoVC(s, e.UserID, e.GuildID, vsNew.ChannelID); err != nil { return } @@ -69,14 +70,14 @@ func (l *ListenerAutoVoice) Handler(s *discordgo.Session, e *discordgo.VoiceStat return } } - } else if autovcCache[e.UserID] != "" { + } else if l.autovcCache[e.UserID] != "" { if err := l.deleteAutoVC(s, e.UserID); err != nil { return } } } else if vsOld != nil && vsNew.ChannelID == "" { - if autovcCache[e.UserID] != "" { + if l.autovcCache[e.UserID] != "" { if err := l.deleteAutoVC(s, e.UserID); err != nil { return } @@ -105,7 +106,7 @@ func (l *ListenerAutoVoice) createAutoVC(s *discordgo.Session, userID, guildID, if err != nil { return err } - autovcCache[userID] = ch.ID + l.autovcCache[userID] = ch.ID if err := s.GuildMemberMove(guildID, userID, &ch.ID); err != nil { return err } @@ -113,11 +114,11 @@ func (l *ListenerAutoVoice) createAutoVC(s *discordgo.Session, userID, guildID, } func (l *ListenerAutoVoice) deleteAutoVC(s *discordgo.Session, userID string) error { - vcID := autovcCache[userID] + vcID := l.autovcCache[userID] _, err := s.ChannelDelete(vcID) if err != nil { return err } - delete(autovcCache, userID) + delete(l.autovcCache, userID) return nil }