WIP fluxer bot
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

initial commit | working basic reaction roles with supervisor and pog db using squirrel

nnuuvv deec7d17

+2119
+61
flake.lock
··· 1 + { 2 + "nodes": { 3 + "nixpkgs": { 4 + "locked": { 5 + "lastModified": 1771369470, 6 + "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", 7 + "owner": "NixOS", 8 + "repo": "nixpkgs", 9 + "rev": "0182a361324364ae3f436a63005877674cf45efb", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "NixOS", 14 + "ref": "nixos-unstable", 15 + "repo": "nixpkgs", 16 + "type": "github" 17 + } 18 + }, 19 + "root": { 20 + "inputs": { 21 + "nixpkgs": "nixpkgs", 22 + "utils": "utils" 23 + } 24 + }, 25 + "systems": { 26 + "locked": { 27 + "lastModified": 1681028828, 28 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 29 + "owner": "nix-systems", 30 + "repo": "default", 31 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 32 + "type": "github" 33 + }, 34 + "original": { 35 + "owner": "nix-systems", 36 + "repo": "default", 37 + "type": "github" 38 + } 39 + }, 40 + "utils": { 41 + "inputs": { 42 + "systems": "systems" 43 + }, 44 + "locked": { 45 + "lastModified": 1731533236, 46 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 47 + "owner": "numtide", 48 + "repo": "flake-utils", 49 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 50 + "type": "github" 51 + }, 52 + "original": { 53 + "owner": "numtide", 54 + "repo": "flake-utils", 55 + "type": "github" 56 + } 57 + } 58 + }, 59 + "root": "root", 60 + "version": 7 61 + }
+32
flake.nix
··· 1 + { 2 + description = "dev env"; 3 + 4 + inputs = { 5 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 + utils.url = "github:numtide/flake-utils"; 7 + }; 8 + 9 + outputs = { self, nixpkgs, utils }: 10 + utils.lib.eachDefaultSystem (system: 11 + let 12 + pkgs = import nixpkgs { inherit system; }; 13 + in 14 + { 15 + devShells.default = pkgs.mkShell { 16 + buildInputs = with pkgs; [ 17 + gleam 18 + erlang_28 19 + rebar3 20 + just 21 + docker 22 + ]; 23 + 24 + shellHook = '' 25 + echo "dev env loaded" 26 + just --list 27 + echo "Use just to run them." 28 + ''; 29 + }; 30 + }); 31 + } 32 +
+25
justfile
··· 1 + # set database variables 2 + export DATABASE_URL := "postgres://fluxer_bot:fluxer_bot@localhost:5432/fluxer_bot" 3 + 4 + # load .env stuff 5 + set dotenv-path := "./server/.envrc" 6 + set dotenv-load := true 7 + 8 + default: 9 + @just --list 10 + 11 + start-db: 12 + mkdir -p data/postgres 13 + docker stop fluxer-bot-dev-pg >> /dev/null || true 14 + docker rm fluxer-bot-dev-pg >> /dev/null || true 15 + docker run -v ./data/postgres:/var/lib/postgresql:Z \ 16 + --name fluxer-bot-dev-pg -p 5432:5432 \ 17 + -e POSTGRES_PASSWORD=fluxer_bot \ 18 + -e POSTGRES_USER=fluxer_bot -d \ 19 + docker.io/library/postgres:latest 20 + 21 + dev: start-db 22 + cd server && gleam run 23 + 24 + update-squirrel: start-db 25 + cd server && gleam run -m squirrel
+2
server/.envrc
··· 1 + export FLUXER_BOT_SECRET=4icM_H1ZOlgb0COLbBNxUFDe8Ce2KrTw0AQXyAh7_9Q 2 + export FLUXER_BOT_TOKEN=1473696954027552804.fcpAlTzexwiDhZtXIVTxbQPO5SBqAl80eHo1wIiA9zg
+23
server/.github/workflows/test.yml
··· 1 + name: test 2 + 3 + on: 4 + push: 5 + branches: 6 + - master 7 + - main 8 + pull_request: 9 + 10 + jobs: 11 + test: 12 + runs-on: ubuntu-latest 13 + steps: 14 + - uses: actions/checkout@v4 15 + - uses: erlef/setup-beam@v1 16 + with: 17 + otp-version: "28" 18 + gleam-version: "1.14.0" 19 + rebar3-version: "3" 20 + # elixir-version: "1" 21 + - run: gleam deps download 22 + - run: gleam test 23 + - run: gleam format --check src test
+4
server/.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+24
server/README.md
··· 1 + # server 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/server)](https://hex.pm/packages/server) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/server/) 5 + 6 + ```sh 7 + gleam add server@1 8 + ``` 9 + ```gleam 10 + import server 11 + 12 + pub fn main() -> Nil { 13 + // TODO: An example of the project in use 14 + } 15 + ``` 16 + 17 + Further documentation can be found at <https://hexdocs.pm/server>. 18 + 19 + ## Development 20 + 21 + ```sh 22 + gleam run # Run the project 23 + gleam test # Run the tests 24 + ```
+27
server/gleam.toml
··· 1 + name = "server" 2 + version = "1.0.0" 3 + 4 + # Fill out these fields if you intend to generate HTML documentation or publish 5 + # your project to the Hex package manager. 6 + # 7 + # description = "" 8 + # licences = ["Apache-2.0"] 9 + # repository = { type = "github", user = "", repo = "" } 10 + # links = [{ title = "Website", href = "" }] 11 + # 12 + # For a full reference of all the available options, you can have a look at 13 + # https://gleam.run/writing-gleam/gleam-toml/. 14 + 15 + [dependencies] 16 + gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 + grom = { path = "../grom/"} 18 + logging = ">= 1.3.0 and < 2.0.0" 19 + envoy = ">= 1.1.0 and < 2.0.0" 20 + gleam_erlang = ">= 1.3.0 and < 2.0.0" 21 + pog = ">= 4.1.0 and < 5.0.0" 22 + splitter = ">= 1.2.0 and < 2.0.0" 23 + crew = ">= 2.0.0 and < 3.0.0" 24 + 25 + [dev-dependencies] 26 + gleeunit = ">= 1.0.0 and < 2.0.0" 27 + squirrel = ">= 4.6.0 and < 5.0.0"
+60
server/manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, 6 + { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, 7 + { name = "crew", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_deque", "gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "crew", source = "hex", outer_checksum = "C6A81CDF01E4413E1EFBFBEF54CA6581D9C243A70912D43CF277BB54FE479725" }, 8 + { name = "envoy", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "850DA9D29D2E5987735872A2B5C81035146D7FE19EFC486129E44440D03FD832" }, 9 + { name = "eval", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "eval", source = "hex", outer_checksum = "264DAF4B49DF807F303CA4A4E4EBC012070429E40BE384C58FE094C4958F9BDA" }, 10 + { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, 11 + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 12 + { name = "glam", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "237C2CE218A2A0A5D46D625F8EF5B78F964BC91018B78D692B17E1AB84295229" }, 13 + { name = "gleam_community_ansi", version = "1.4.4", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "1B3AEA6074AB34D5F0674744F36DDC7290303A03295507E2DEC61EDD6F5777FE" }, 14 + { name = "gleam_community_colour", version = "2.0.4", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "6DB4665555D7D2B27F0EA32EF47E8BEBC4303821765F9C73D483F38EE24894F0" }, 15 + { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" }, 16 + { name = "gleam_deque", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_deque", source = "hex", outer_checksum = "64D77068931338CF0D0CB5D37522C3E3CCA7CB7D6C5BACB41648B519CC0133C7" }, 17 + { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, 18 + { name = "gleam_http", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "82EA6A717C842456188C190AFB372665EA56CE13D8559BF3B1DD9E40F619EE0C" }, 19 + { name = "gleam_httpc", version = "5.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "C545172618D07811494E97AAA4A0FB34DA6F6D0061FDC8041C2F8E3BE2B2E48F" }, 20 + { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, 21 + { name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" }, 22 + { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" }, 23 + { name = "gleam_stdlib", version = "0.69.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "AAB0962BEBFAA67A2FBEE9EEE218B057756808DC9AF77430F5182C6115B3A315" }, 24 + { name = "gleam_time", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "56DB0EF9433826D3B99DB0B4AF7A2BFED13D09755EC64B1DAAB46F804A9AD47D" }, 25 + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, 26 + { name = "glexer", version = "2.3.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "splitter"], otp_app = "glexer", source = "hex", outer_checksum = "41D8D2E855AEA87ADC94B7AF26A5FEA3C90268D4CF2CCBBD64FD6863714EE085" }, 27 + { name = "gramps", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "8B7195978FBFD30B43DF791A8A272041B81E45D245314D7A41FC57237AA882A0" }, 28 + { name = "grom", version = "5.1.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_httpc", "gleam_json", "gleam_otp", "gleam_stdlib", "gleam_time", "multipart_form", "operating_system", "splitter", "status_code", "stratus"], source = "local", path = "../grom" }, 29 + { name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" }, 30 + { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, 31 + { name = "mug", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "mug", source = "hex", outer_checksum = "C01279D98E40371DA23461774B63F0E3581B8F1396049D881B0C7EB32799D93F" }, 32 + { name = "multipart_form", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib"], otp_app = "multipart_form", source = "hex", outer_checksum = "082C77A0C3BB1128FCD55491665E9B72BC943E849B67D02B08CFA6808AD8E47C" }, 33 + { name = "non_empty_list", version = "2.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "non_empty_list", source = "hex", outer_checksum = "1CA43D18C07E98E9ED5A60D9CB2FFE0FF40DEFFA45D58A3FF589589F05658F7B" }, 34 + { name = "opentelemetry_api", version = "1.5.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "F53EC8A1337AE4A487D43AC89DA4BD3A3C99DDF576655D071DEED8B56A2D5DDA" }, 35 + { name = "operating_system", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "operating_system", source = "hex", outer_checksum = "682D4D19496E607A6E6229AA51A38A1400ED5067C2C54D56843DBCB0C61FFA6D" }, 36 + { name = "pg_types", version = "0.6.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "9949A4849DD13408FA249AB7B745E0D2DFDB9532AEE2B9722326E33CD082A778" }, 37 + { name = "pgo", version = "0.20.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "2F11E6649CEB38E569EF56B16BE1D04874AE5B11A02867080A2817CE423C683B" }, 38 + { name = "pog", version = "4.1.0", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_otp", "gleam_stdlib", "gleam_time", "pgo"], otp_app = "pog", source = "hex", outer_checksum = "E4AFBA39A5FAA2E77291836C9683ADE882E65A06AB28CA7D61AE7A3AD61EBBD5" }, 39 + { name = "simplifile", version = "2.3.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "E049B4DACD4D206D87843BCF4C775A50AE0F50A52031A2FFB40C9ED07D6EC70A" }, 40 + { name = "splitter", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "3DFD6B6C49E61EDAF6F7B27A42054A17CFF6CA2135FF553D0CB61C234D281DD0" }, 41 + { name = "squirrel", version = "4.6.0", build_tools = ["gleam"], requirements = ["argv", "envoy", "eval", "filepath", "glam", "gleam_community_ansi", "gleam_crypto", "gleam_json", "gleam_regexp", "gleam_stdlib", "gleam_time", "glexer", "justin", "mug", "non_empty_list", "pog", "simplifile", "term_size", "tom", "tote", "youid"], otp_app = "squirrel", source = "hex", outer_checksum = "0ED10A868BDD1A5D4B68D99CD1C72DC3F23C6E36E16D33454C5F0C31BAC9CB1E" }, 42 + { name = "status_code", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "status_code", source = "hex", outer_checksum = "F6EC765345A634602AD030EB1B4D34A425873CBC223CEC595EBF062073CD70BC" }, 43 + { name = "stratus", version = "2.0.0", build_tools = ["gleam"], requirements = ["exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "gramps", "logging"], otp_app = "stratus", source = "hex", outer_checksum = "DB81236C1E00C29C01FBED9D3B0946E7A74AEE7D515036C7BF152D22430ADE9E" }, 44 + { name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" }, 45 + { name = "tom", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time"], otp_app = "tom", source = "hex", outer_checksum = "90791DA4AACE637E30081FE77049B8DB850FBC8CACC31515376BCC4E59BE1DD2" }, 46 + { name = "tote", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tote", source = "hex", outer_checksum = "A249892E26A53C668897F8D47845B0007EEE07707A1A03437487F0CD5A452CA5" }, 47 + { name = "youid", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_stdlib", "gleam_time"], otp_app = "youid", source = "hex", outer_checksum = "580E909FD704DB16416D5CB080618EDC2DA0F1BE4D21B490C0683335E3FFC5AF" }, 48 + ] 49 + 50 + [requirements] 51 + crew = { version = ">= 2.0.0 and < 3.0.0" } 52 + envoy = { version = ">= 1.1.0 and < 2.0.0" } 53 + gleam_erlang = { version = ">= 1.3.0 and < 2.0.0" } 54 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 55 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 56 + grom = { path = "../grom/" } 57 + logging = { version = ">= 1.3.0 and < 2.0.0" } 58 + pog = { version = ">= 4.1.0 and < 5.0.0" } 59 + splitter = { version = ">= 1.2.0 and < 2.0.0" } 60 + squirrel = { version = ">= 4.6.0 and < 5.0.0" }
+460
server/src/server.gleam
··· 1 + import envoy 2 + import gleam/erlang/process 3 + import gleam/int 4 + import gleam/list 5 + import gleam/option.{None, Some} 6 + import gleam/otp/actor 7 + import gleam/otp/static_supervisor.{type Supervisor} as supervisor 8 + import gleam/otp/supervision 9 + import gleam/result 10 + import gleam/string 11 + import gramps/http 12 + import grom 13 + import grom/application 14 + import grom/gateway 15 + import grom/gateway/intent 16 + import grom/guild 17 + import grom/guild_member 18 + import grom/message 19 + import grom/message/message_reference 20 + import grom/message/reaction 21 + import grom/permission 22 + import grom/user 23 + import grom/user/current_user 24 + import logging 25 + import pog 26 + import server/emoji 27 + import server/reaction_role 28 + import server/reaction_role/database 29 + import splitter 30 + 31 + type State { 32 + State( 33 + client: grom.Client, 34 + db_pool_name: process.Name(pog.Message), 35 + self: user.User, 36 + ) 37 + } 38 + 39 + pub fn main() -> Nil { 40 + logging.configure() 41 + 42 + let assert Ok(db_config) = database_config() 43 + 44 + let assert Ok(bot_config) = bot_config(db_config.pool_name) 45 + 46 + let assert Ok(_) = 47 + supervisor.new(supervisor.OneForOne) 48 + |> supervisor.add(pog.supervised(db_config)) 49 + |> supervisor.add(gateway.supervised(bot_config)) 50 + |> supervisor.start() 51 + 52 + let assert Ok(_) = setup_db(db_config.pool_name) as "Failed to set up db" 53 + 54 + process.sleep_forever() 55 + } 56 + 57 + fn on_event(state: State, event: gateway.Event) -> gateway.Next(State) { 58 + case event { 59 + gateway.ErrorEvent(error) -> { 60 + logging.log(logging.Warning, string.inspect(error)) 61 + gateway.continue(state) 62 + } 63 + 64 + gateway.AllShardsReadyEvent(ready) -> on_ready(state, ready) 65 + gateway.MessageCreatedEvent(message) -> on_message(state, message) 66 + gateway.MessageReactionCreatedEvent(reaction) -> 67 + on_new_reaction(state, reaction) 68 + 69 + _ -> log_and_continue(event, state) 70 + } 71 + } 72 + 73 + type NewReactionError { 74 + // TODO: once i get this working i should see if that is ever actually the case or if i can just change that in grom 75 + /// this should, in theory, not happen but grom parses it into an `Option(String)` 76 + GuildIdMissing 77 + /// Normal if there is no reaction role associated with a specific message (the vast majority) 78 + NoReactionRoleFound 79 + InvalidReaction 80 + FailedToAddRole(AddRoleError) 81 + } 82 + 83 + type AddRoleError { 84 + /// let user know that to assign roles, the bot needs the correct permissions AND the to-assign role need to be below the bot-role 85 + InsufficientPermission 86 + Other 87 + } 88 + 89 + fn on_new_reaction( 90 + state: State, 91 + reaction: gateway.MessageReactionCreatedMessage, 92 + ) -> gateway.Next(State) { 93 + case reaction.user_id { 94 + user_id if user_id == state.self.id -> gateway.continue(state) 95 + 96 + _ -> { 97 + let _ = 98 + do_reaction_role(state, reaction) 99 + |> cleanup_failed_reaction_role(state, reaction) 100 + 101 + gateway.continue(state) 102 + } 103 + } 104 + } 105 + 106 + fn cleanup_failed_reaction_role( 107 + reaction_role: Result(Nil, NewReactionError), 108 + state: State, 109 + reaction: gateway.MessageReactionCreatedMessage, 110 + ) -> a { 111 + todo 112 + } 113 + 114 + fn do_reaction_role( 115 + state: State, 116 + reaction: gateway.MessageReactionCreatedMessage, 117 + ) -> Result(Nil, NewReactionError) { 118 + use guild_id <- result.try( 119 + reaction.guild_id |> option.to_result(GuildIdMissing), 120 + ) 121 + let channel_id = reaction.channel_id 122 + let message_id = reaction.message_id 123 + 124 + use reactioin_roles <- result.try( 125 + database.find( 126 + state.db_pool_name, 127 + database.MessageRef(guild_id:, channel_id:, message_id:), 128 + ) 129 + |> result.replace_error(NoReactionRoleFound), 130 + ) 131 + 132 + use reactioin_role <- result.try( 133 + reactioin_roles 134 + |> list.find(fn(role) { Some(role.emote) == reaction.emoji.name }) 135 + |> result.replace_error(InvalidReaction), 136 + ) 137 + 138 + use _ <- result.try( 139 + guild_member.add_role( 140 + state.client, 141 + in: guild_id, 142 + to: reaction.user_id, 143 + id: reactioin_role.target_role_id, 144 + because: Some("reaction role"), 145 + ) 146 + |> result.map_error(fn(error) { 147 + case error { 148 + grom.InsufficientPermissions -> FailedToAddRole(InsufficientPermission) 149 + _ -> FailedToAddRole(Other) 150 + } 151 + }), 152 + ) 153 + 154 + Ok(Nil) 155 + } 156 + 157 + fn log_and_continue(event: gateway.Event, state: State) -> gateway.Next(State) { 158 + logging.log(logging.Info, event |> string.inspect()) 159 + gateway.continue(state) 160 + } 161 + 162 + fn on_message( 163 + state: State, 164 + message: gateway.MessageCreatedMessage, 165 + ) -> gateway.Next(State) { 166 + { 167 + let gateway.MessageCreatedMessage(message:, guild_id:, member:, mentions:) = 168 + message 169 + let self = state.self 170 + use guild_id <- option.then(guild_id) 171 + use member <- option.then(member) 172 + 173 + handle_call_and_response(state.client, message, guild_id) 174 + 175 + let is_admin = member_is_admin(state.client, guild_id:, member:) 176 + 177 + case mentions, message { 178 + // bot is the first mentioned user *AND* the sender is an admin 179 + [mentioned_user, ..], message.Message(author:, ..) 180 + if mentioned_user.id == self.id && is_admin 181 + -> { 182 + let splitter = splitter.new([" "]) 183 + 184 + let #(_at_self, _, rest) = splitter.split(splitter, message.content) 185 + 186 + let _ = case rest |> string.trim() |> echo |> string.split(" ") { 187 + ["add-reaction-role", role_name, emote, add_to_message_link] -> { 188 + use emote <- result.try( 189 + emoji.from_string(emote) |> result.replace_error(InvalidEmote), 190 + ) 191 + 192 + // TODO: respond depending on error 193 + add_reaction_role( 194 + state, 195 + guild_id, 196 + role_name, 197 + emote, 198 + add_to_message_link, 199 + ) 200 + |> echo 201 + } 202 + ["fix-reaction-roles"] -> todo as "fix reaction roles" 203 + ["help"] -> todo as "help text" 204 + 205 + _ -> todo as "respond with help text" 206 + } 207 + 208 + None 209 + } 210 + 211 + _, _ -> None 212 + } 213 + None 214 + } 215 + gateway.continue(state) 216 + } 217 + 218 + type AddReactionRoleError { 219 + FailedToGetRoles 220 + FailedToFindRole 221 + InvalidEmote 222 + InvalidLink 223 + FailedToPersist 224 + FailedToAddReaction 225 + } 226 + 227 + fn add_reaction_role( 228 + state state: State, 229 + guild_id guild_id: String, 230 + role_name role_name: String, 231 + emote emote: String, 232 + to_message add_to_message_link: String, 233 + ) -> Result(Nil, AddReactionRoleError) { 234 + use roles <- result.try( 235 + guild.get_roles(state.client, guild_id) 236 + |> result.replace_error(FailedToGetRoles), 237 + ) 238 + 239 + use target_role <- result.try( 240 + roles 241 + |> list.find(fn(role) { role.name == role_name }) 242 + |> result.replace_error(FailedToFindRole), 243 + ) 244 + 245 + case string.split(add_to_message_link, "/") |> list.reverse() { 246 + [message_id, channel_id, ..] -> { 247 + use _ <- result.try( 248 + persist_reaction_role( 249 + state, 250 + reaction_role.ReactionRole( 251 + guild_id, 252 + channel_id, 253 + message_id, 254 + emote, 255 + target_role.id, 256 + ), 257 + ) 258 + |> result.replace_error(FailedToPersist), 259 + ) 260 + 261 + use _ <- result.try( 262 + reaction.create( 263 + state.client, 264 + in: channel_id, 265 + on: message_id, 266 + emoji: emote, 267 + ) 268 + |> result.replace_error(FailedToAddReaction), 269 + ) 270 + Ok(Nil) 271 + } 272 + 273 + _ -> Error(InvalidLink) 274 + } 275 + } 276 + 277 + fn persist_reaction_role( 278 + state: State, 279 + reaction_role: reaction_role.ReactionRole, 280 + ) -> Result(pog.Returned(Nil), pog.QueryError) { 281 + database.insert(state.db_pool_name, reaction_role) 282 + } 283 + 284 + fn handle_call_and_response( 285 + client: grom.Client, 286 + message: message.Message, 287 + guild_id: String, 288 + ) -> Nil { 289 + case message.content { 290 + "hello little stylus" -> { 291 + let _response = 292 + message.create( 293 + client, 294 + in: message.channel_id, 295 + using: message.Create( 296 + ..message.new_create(), 297 + content: Some("hello mario"), 298 + message_reference: Some(message_reference.MessageReference( 299 + type_: message_reference.Default, 300 + message_id: Some(message.id), 301 + channel_id: Some(message.channel_id), 302 + guild_id: Some(guild_id), 303 + fail_if_not_exists: Some(False), 304 + )), 305 + ), 306 + ) 307 + let _ = 308 + send_ephemeral_message( 309 + client:, 310 + channel_id: message.channel_id, 311 + message: message.Create( 312 + ..message.new_create(), 313 + content: Some("noone will believe you >:3"), 314 + message_reference: Some(message_reference.MessageReference( 315 + type_: message_reference.Default, 316 + message_id: Some(message.id), 317 + channel_id: Some(message.channel_id), 318 + guild_id: Some(guild_id), 319 + fail_if_not_exists: Some(False), 320 + )), 321 + ), 322 + delete_after_ms: 10_000, 323 + ) 324 + 325 + Nil 326 + } 327 + _ -> Nil 328 + } 329 + } 330 + 331 + fn on_ready( 332 + state: State, 333 + ready: gateway.AllShardsReadyMessage, 334 + ) -> gateway.Next(State) { 335 + logging.log(logging.Info, "Ready!") 336 + 337 + logging.log(logging.Info, ready |> string.inspect()) 338 + 339 + gateway.continue(State(..state, self: ready.user)) 340 + } 341 + 342 + /// attempts to send a `message`, if that succeeds spawns an unlinked process that will attempt to delete the message after the supplied cooldown `delete_after_ms` 343 + /// 344 + fn send_ephemeral_message( 345 + client client: grom.Client, 346 + channel_id in: String, 347 + message message: message.Create, 348 + delete_after_ms delete_after_ms: Int, 349 + ) -> Result(Nil, grom.Error) { 350 + use sent <- result.try(message.create(client, in:, using: message)) 351 + 352 + process.spawn_unlinked(fn() { 353 + process.sleep(delete_after_ms) 354 + 355 + use _ <- result.try(message.delete( 356 + client, 357 + in: sent.channel_id, 358 + id: sent.id, 359 + because: Some("Ephemeral message"), 360 + )) 361 + Ok(Nil) 362 + }) 363 + 364 + Ok(Nil) 365 + } 366 + 367 + fn member_is_admin( 368 + client client: grom.Client, 369 + guild_id guild_id: String, 370 + member member: guild_member.GuildMember, 371 + ) -> Bool { 372 + let roles = guild.get_roles(client, guild_id) 373 + 374 + case roles { 375 + Ok(roles) -> 376 + roles 377 + |> list.any(fn(role) { 378 + member.roles |> list.contains(role.id) 379 + && role.permissions |> list.contains(permission.Administrator) 380 + }) 381 + Error(error) -> { 382 + logging.log( 383 + logging.Warning, 384 + "Failed to get roles for guild: " 385 + <> guild_id 386 + <> " with error: " 387 + <> string.inspect(error), 388 + ) 389 + False 390 + } 391 + } 392 + } 393 + 394 + // setup ------------------------------------------------------------------------ 395 + 396 + fn database_config() -> Result(pog.Config, String) { 397 + use database_url <- result.try( 398 + envoy.get("DATABASE_URL") |> result.replace_error("Missing DATABASE_URL"), 399 + ) 400 + 401 + let pool_name = process.new_name("db_pool") 402 + 403 + use config <- result.try( 404 + pog.url_config(pool_name, database_url) 405 + |> result.replace_error("Invalid DATABASE_URL"), 406 + ) 407 + 408 + config 409 + |> pog.pool_size(10) 410 + |> Ok 411 + } 412 + 413 + fn setup_db( 414 + db_pool_name db_pool_name: process.Name(pog.Message), 415 + ) -> Result(pog.Returned(Nil), pog.QueryError) { 416 + database.create(db_pool_name) 417 + } 418 + 419 + fn bot_config( 420 + db_pool_name: process.Name(pog.Message), 421 + ) -> Result(gateway.Builder(State), actor.StartError) { 422 + let assert Ok(bot_token) = envoy.get("FLUXER_BOT_TOKEN") 423 + 424 + let client = grom.Client(bot_token) 425 + 426 + let identify = gateway.identify(client, intents: intent.all) 427 + 428 + use data <- result.try( 429 + gateway.get_data(client) 430 + |> log_error(logging.Critical, "Failed to get gateway data") 431 + |> result.replace_error(actor.InitFailed("Failed to get gateway data")), 432 + ) 433 + use self <- result.try( 434 + current_user.get(client) 435 + |> log_error(logging.Critical, "Failed to get self") 436 + |> result.replace_error(actor.InitFailed("Failed to get self")), 437 + ) 438 + 439 + gateway.new(State(client:, db_pool_name:, self:), identify, data) 440 + |> gateway.on_event(do: on_event) 441 + |> gateway.on_close(fn(_state) { 442 + logging.log(logging.Alert, "Gateway closed") 443 + }) 444 + |> Ok 445 + } 446 + 447 + /// `log_level` -> to log with 448 + /// `message` -> to prepend to `string.inspect(error)` 449 + /// 450 + fn log_error( 451 + result: Result(a, b), 452 + log_level log_level: logging.LogLevel, 453 + message message: String, 454 + ) -> Result(a, b) { 455 + let _ = 456 + result.map_error(result, fn(error) { 457 + logging.log(log_level, message <> " " <> string.inspect(error)) 458 + }) 459 + result 460 + }
+1147
server/src/server/emoji.gleam
··· 1 + /// convert `:emote:` string to their associated unicode emoji 2 + /// 3 + pub fn from_string(emote_string: String) -> Result(String, Nil) { 4 + case emote_string { 5 + ":bowtie:" -> Ok("🎀") 6 + ":smile:" -> Ok("😄") 7 + ":laughing:" -> Ok("😆") 8 + ":blush:" -> Ok("😊") 9 + ":smiley:" -> Ok("😃") 10 + ":relaxed:" -> Ok("☺️") 11 + ":smirk:" -> Ok("😏") 12 + ":heart_eyes:" -> Ok("😍") 13 + ":kissing_heart:" -> Ok("😘") 14 + ":kissing_closed_eyes:" -> Ok("😚") 15 + ":flushed:" -> Ok("😳") 16 + ":relieved:" -> Ok("😌") 17 + ":satisfied:" -> Ok("😌") 18 + ":grin:" -> Ok("😁") 19 + ":wink:" -> Ok("😉") 20 + ":stuck_out_tongue_winking_eye:" -> Ok("😜") 21 + ":stuck_out_tongue_closed_eyes:" -> Ok("😝") 22 + ":grinning:" -> Ok("😀") 23 + ":kissing:" -> Ok("😗") 24 + ":kissing_smiling_eyes:" -> Ok("😙") 25 + ":stuck_out_tongue:" -> Ok("😛") 26 + ":sleeping:" -> Ok("😴") 27 + ":worried:" -> Ok("😟") 28 + ":frowning:" -> Ok("😦") 29 + ":anguished:" -> Ok("😧") 30 + ":open_mouth:" -> Ok("😮") 31 + ":grimacing:" -> Ok("😬") 32 + ":confused:" -> Ok("😕") 33 + ":hushed:" -> Ok("😯") 34 + ":expressionless:" -> Ok("😑") 35 + ":unamused:" -> Ok("😒") 36 + ":sweat_smile:" -> Ok("😅") 37 + ":sweat:" -> Ok("😓") 38 + ":disappointed_relieved:" -> Ok("😥") 39 + ":weary:" -> Ok("😩") 40 + ":pensive:" -> Ok("😔") 41 + ":disappointed:" -> Ok("😞") 42 + ":confounded:" -> Ok("😖") 43 + ":fearful:" -> Ok("😨") 44 + ":cold_sweat:" -> Ok("😰") 45 + ":persevere:" -> Ok("😣") 46 + ":cry:" -> Ok("😢") 47 + ":sob:" -> Ok("😭") 48 + ":joy:" -> Ok("😂") 49 + ":astonished:" -> Ok("😲") 50 + ":scream:" -> Ok("😱") 51 + ":neckbeard:" -> Ok("🧔") 52 + ":tired_face:" -> Ok("😫") 53 + ":angry:" -> Ok("😠") 54 + ":rage:" -> Ok("😡") 55 + ":triumph:" -> Ok("😤") 56 + ":sleepy:" -> Ok("😪") 57 + ":yum:" -> Ok("😋") 58 + ":mask:" -> Ok("😷") 59 + ":sunglasses:" -> Ok("🕶️") 60 + ":dizzy_face:" -> Ok("😵") 61 + ":imp:" -> Ok("👿") 62 + ":smiling_imp:" -> Ok("😈") 63 + ":neutral_face:" -> Ok("😐") 64 + ":no_mouth:" -> Ok("😶") 65 + ":innocent:" -> Ok("😇") 66 + ":alien:" -> Ok("👽") 67 + ":yellow_heart:" -> Ok("💛") 68 + ":blue_heart:" -> Ok("💙") 69 + ":purple_heart:" -> Ok("💜") 70 + ":heart:" -> Ok("❤️") 71 + ":green_heart:" -> Ok("💚") 72 + ":broken_heart:" -> Ok("💔") 73 + ":heartbeat:" -> Ok("💓") 74 + ":heartpulse:" -> Ok("💗") 75 + ":two_hearts:" -> Ok("💕") 76 + ":revolving_hearts:" -> Ok("💞") 77 + ":cupid:" -> Ok("💘") 78 + ":sparkling_heart:" -> Ok("💖") 79 + ":sparkles:" -> Ok("✨") 80 + ":star:" -> Ok("⭐") 81 + ":star2:" -> Ok("🌟") 82 + ":dizzy:" -> Ok("💫") 83 + ":boom:" -> Ok("💥") 84 + ":collision:" -> Ok("💥") 85 + ":anger:" -> Ok("💢") 86 + ":exclamation:" -> Ok("❗") 87 + ":question:" -> Ok("❓") 88 + ":grey_exclamation:" -> Ok("❕") 89 + ":grey_question:" -> Ok("❔") 90 + ":zzz:" -> Ok("💤") 91 + ":dash:" -> Ok("💨") 92 + ":sweat_drops:" -> Ok("💦") 93 + ":notes:" -> Ok("🎶") 94 + ":musical_note:" -> Ok("🎵") 95 + ":fire:" -> Ok("🔥") 96 + ":hankey:" -> Ok("💩") 97 + ":poop:" -> Ok("💩") 98 + ":shit:" -> Ok("💩") 99 + ":+1:" -> Ok("👍") 100 + ":thumbsup:" -> Ok("👍") 101 + ":-1:" -> Ok("👎") 102 + ":thumbsdown:" -> Ok("👎") 103 + ":ok_hand:" -> Ok("👌") 104 + ":punch:" -> Ok("👊") 105 + ":facepunch:" -> Ok("👊") 106 + ":fist:" -> Ok("✊") 107 + ":v:" -> Ok("✌️") 108 + ":wave:" -> Ok("👋") 109 + ":hand:" -> Ok("✋") 110 + ":raised_hand:" -> Ok("✋") 111 + ":open_hands:" -> Ok("👐") 112 + ":point_up:" -> Ok("☝️") 113 + ":point_down:" -> Ok("👇") 114 + ":point_left:" -> Ok("👈") 115 + ":point_right:" -> Ok("👉") 116 + ":raised_hands:" -> Ok("🙌") 117 + ":pray:" -> Ok("🙏") 118 + ":point_up_2:" -> Ok("👆") 119 + ":clap:" -> Ok("👏") 120 + ":muscle:" -> Ok("💪") 121 + ":metal:" -> Ok("🤘") 122 + ":fu:" -> Ok("🖕") 123 + ":walking:" -> Ok("🚶") 124 + ":runner:" -> Ok("🏃") 125 + ":running:" -> Ok("🏃") 126 + ":couple:" -> Ok("👫") 127 + ":family:" -> Ok("👪") 128 + ":two_men_holding_hands:" -> Ok("👬") 129 + ":two_women_holding_hands:" -> Ok("👭") 130 + ":dancer:" -> Ok("💃") 131 + ":dancers:" -> Ok("👯") 132 + ":ok_woman:" -> Ok("🙆") 133 + ":no_good:" -> Ok("🙅") 134 + ":information_desk_person:" -> Ok("💁") 135 + ":raising_hand:" -> Ok("🙋") 136 + ":bride_with_veil:" -> Ok("👰") 137 + ":person_with_pouting_face:" -> Ok("🙎") 138 + ":person_frowning:" -> Ok("🙍") 139 + ":bow:" -> Ok("🙇") 140 + ":couplekiss:" -> Ok("💏") 141 + ":couple_with_heart:" -> Ok("💑") 142 + ":massage:" -> Ok("💆") 143 + ":haircut:" -> Ok("💇") 144 + ":nail_care:" -> Ok("💅") 145 + ":boy:" -> Ok("👦") 146 + ":girl:" -> Ok("👧") 147 + ":woman:" -> Ok("👩") 148 + ":man:" -> Ok("👨") 149 + ":baby:" -> Ok("👶") 150 + ":older_woman:" -> Ok("👵") 151 + ":older_man:" -> Ok("👴") 152 + ":person_with_blond_hair:" -> Ok("👱") 153 + ":man_with_gua_pi_mao:" -> Ok("👲") 154 + ":man_with_turban:" -> Ok("👳") 155 + ":construction_worker:" -> Ok("👷") 156 + ":cop:" -> Ok("👮") 157 + ":angel:" -> Ok("👼") 158 + ":princess:" -> Ok("👸") 159 + ":smiley_cat:" -> Ok("😺") 160 + ":smile_cat:" -> Ok("😸") 161 + ":heart_eyes_cat:" -> Ok("😻") 162 + ":kissing_cat:" -> Ok("😽") 163 + ":smirk_cat:" -> Ok("😼") 164 + ":scream_cat:" -> Ok("🙀") 165 + ":crying_cat_face:" -> Ok("😿") 166 + ":joy_cat:" -> Ok("😹") 167 + ":pouting_cat:" -> Ok("😾") 168 + ":japanese_ogre:" -> Ok("👹") 169 + ":japanese_goblin:" -> Ok("👺") 170 + ":see_no_evil:" -> Ok("🙈") 171 + ":hear_no_evil:" -> Ok("🙉") 172 + ":speak_no_evil:" -> Ok("🙊") 173 + ":guardsman:" -> Ok("💂") 174 + ":skull:" -> Ok("💀") 175 + ":feet:" -> Ok("🐾") 176 + ":lips:" -> Ok("👄") 177 + ":kiss:" -> Ok("💋") 178 + ":droplet:" -> Ok("💧") 179 + ":ear:" -> Ok("👂") 180 + ":eyes:" -> Ok("👀") 181 + ":nose:" -> Ok("👃") 182 + ":tongue:" -> Ok("👅") 183 + ":love_letter:" -> Ok("💌") 184 + ":bust_in_silhouette:" -> Ok("👤") 185 + ":busts_in_silhouette:" -> Ok("👥") 186 + ":speech_balloon:" -> Ok("💬") 187 + ":thought_balloon:" -> Ok("💭") 188 + ":feelsgood:" -> Ok("🤗") 189 + ":finnadie:" -> Ok("🤐") 190 + ":goberserk:" -> Ok("🤯") 191 + ":godmode:" -> Ok("🤩") 192 + ":hurtrealbad:" -> Ok("🤕") 193 + ":rage1:" -> Ok("😡") 194 + ":rage2:" -> Ok("😠") 195 + ":rage3:" -> Ok("💢") 196 + ":rage4:" -> Ok("😤") 197 + ":suspect:" -> Ok("🤔") 198 + ":trollface:" -> Ok("😈") 199 + ":sunny:" -> Ok("☀️") 200 + ":umbrella:" -> Ok("☂️") 201 + ":cloud:" -> Ok("☁️") 202 + ":snowflake:" -> Ok("❄️") 203 + ":snowman:" -> Ok("☃️") 204 + ":zap:" -> Ok("⚡️") 205 + ":cyclone:" -> Ok("🌀") 206 + ":foggy:" -> Ok("🌁") 207 + ":ocean:" -> Ok("🌊") 208 + ":cat:" -> Ok("🐱") 209 + ":dog:" -> Ok("🐶") 210 + ":mouse:" -> Ok("🐭") 211 + ":hamster:" -> Ok("🐹") 212 + ":rabbit:" -> Ok("🐰") 213 + ":wolf:" -> Ok("🐺") 214 + ":frog:" -> Ok("🐸") 215 + ":tiger:" -> Ok("🐯") 216 + ":koala:" -> Ok("🐨") 217 + ":bear:" -> Ok("🐻") 218 + ":pig:" -> Ok("🐷") 219 + ":pig_nose:" -> Ok("🐽") 220 + ":cow:" -> Ok("🐮") 221 + ":boar:" -> Ok("🐗") 222 + ":monkey_face:" -> Ok("🐵") 223 + ":monkey:" -> Ok("🐒") 224 + ":horse:" -> Ok("🐎") 225 + ":racehorse:" -> Ok("🐎") 226 + ":camel:" -> Ok("🐫") 227 + ":sheep:" -> Ok("🐑") 228 + ":elephant:" -> Ok("🐘") 229 + ":panda_face:" -> Ok("🐼") 230 + ":snake:" -> Ok("🐍") 231 + ":bird:" -> Ok("🐦") 232 + ":baby_chick:" -> Ok("🐤") 233 + ":hatched_chick:" -> Ok("🐥") 234 + ":hatching_chick:" -> Ok("🐣") 235 + ":chicken:" -> Ok("🐔") 236 + ":penguin:" -> Ok("🐧") 237 + ":turtle:" -> Ok("🐢") 238 + ":bug:" -> Ok("🐛") 239 + ":honeybee:" -> Ok("🐝") 240 + ":ant:" -> Ok("🐜") 241 + ":beetle:" -> Ok("🐞") 242 + ":snail:" -> Ok("🐌") 243 + ":octopus:" -> Ok("🐙") 244 + ":tropical_fish:" -> Ok("🐠") 245 + ":fish:" -> Ok("🐟") 246 + ":whale:" -> Ok("🐳") 247 + ":whale2:" -> Ok("🐋") 248 + ":dolphin:" -> Ok("🐬") 249 + ":cow2:" -> Ok("🐄") 250 + ":ram:" -> Ok("🐏") 251 + ":rat:" -> Ok("🐀") 252 + ":water_buffalo:" -> Ok("🐃") 253 + ":tiger2:" -> Ok("🐅") 254 + ":rabbit2:" -> Ok("🐇") 255 + ":dragon:" -> Ok("🐉") 256 + ":goat:" -> Ok("🐐") 257 + ":rooster:" -> Ok("🐓") 258 + ":dog2:" -> Ok("🐕") 259 + ":pig2:" -> Ok("🐖") 260 + ":mouse2:" -> Ok("🐁") 261 + ":ox:" -> Ok("🐂") 262 + ":dragon_face:" -> Ok("🐲") 263 + ":blowfish:" -> Ok("🐡") 264 + ":crocodile:" -> Ok("🐊") 265 + ":dromedary_camel:" -> Ok("🐪") 266 + ":leopard:" -> Ok("🐆") 267 + ":cat2:" -> Ok("🐈") 268 + ":poodle:" -> Ok("🐩") 269 + ":paw_prints:" -> Ok("🐾") 270 + ":bouquet:" -> Ok("💐") 271 + ":cherry_blossom:" -> Ok("🌸") 272 + ":tulip:" -> Ok("🌷") 273 + ":four_leaf_clover:" -> Ok("🍀") 274 + ":rose:" -> Ok("🌹") 275 + ":sunflower:" -> Ok("🌻") 276 + ":hibiscus:" -> Ok("🌺") 277 + ":maple_leaf:" -> Ok("🍁") 278 + ":leaves:" -> Ok("🍃") 279 + ":fallen_leaf:" -> Ok("🍂") 280 + ":herb:" -> Ok("🌿") 281 + ":mushroom:" -> Ok("🍄") 282 + ":cactus:" -> Ok("🌵") 283 + ":palm_tree:" -> Ok("🌴") 284 + ":evergreen_tree:" -> Ok("🌲") 285 + ":deciduous_tree:" -> Ok("🌳") 286 + ":chestnut:" -> Ok("🌰") 287 + ":seedling:" -> Ok("🌱") 288 + ":blossom:" -> Ok("🌼") 289 + ":ear_of_rice:" -> Ok("🌾") 290 + ":shell:" -> Ok("🐚") 291 + ":globe_with_meridians:" -> Ok("🌐") 292 + ":sun_with_face:" -> Ok("🌞") 293 + ":full_moon_with_face:" -> Ok("🌝") 294 + ":new_moon_with_face:" -> Ok("🌚") 295 + ":new_moon:" -> Ok("🌑") 296 + ":waxing_crescent_moon:" -> Ok("🌒") 297 + ":first_quarter_moon:" -> Ok("🌓") 298 + ":waxing_gibbous_moon:" -> Ok("🌔") 299 + ":full_moon:" -> Ok("🌕") 300 + ":waning_gibbous_moon:" -> Ok("🌖") 301 + ":last_quarter_moon:" -> Ok("🌗") 302 + ":waning_crescent_moon:" -> Ok("🌘") 303 + ":last_quarter_moon_with_face:" -> Ok("🌜") 304 + ":first_quarter_moon_with_face:" -> Ok("🌛") 305 + ":moon:" -> Ok("🌝") 306 + ":earth_africa:" -> Ok("🌍") 307 + ":earth_americas:" -> Ok("🌎") 308 + ":earth_asia:" -> Ok("🌏") 309 + ":volcano:" -> Ok("🌋") 310 + ":milky_way:" -> Ok("🌌") 311 + ":partly_sunny:" -> Ok("⛅️") 312 + ":octocat:" -> Ok("🐙") 313 + ":squirrel:" -> Ok("🐿️") 314 + ":bamboo:" -> Ok("🎍") 315 + ":gift_heart:" -> Ok("💝") 316 + ":dolls:" -> Ok("🎎") 317 + ":school_satchel:" -> Ok("🎒") 318 + ":mortar_board:" -> Ok("🎓") 319 + ":flags:" -> Ok("🎏") 320 + ":fireworks:" -> Ok("🎆") 321 + ":sparkler:" -> Ok("🎇") 322 + ":wind_chime:" -> Ok("🎐") 323 + ":rice_scene:" -> Ok("🎑") 324 + ":jack_o_lantern:" -> Ok("🎃") 325 + ":ghost:" -> Ok("👻") 326 + ":santa:" -> Ok("🎅") 327 + ":christmas_tree:" -> Ok("🎄") 328 + ":gift:" -> Ok("🎁") 329 + ":bell:" -> Ok("🔔") 330 + ":no_bell:" -> Ok("🔕") 331 + ":tanabata_tree:" -> Ok("🎋") 332 + ":tada:" -> Ok("🎉") 333 + ":confetti_ball:" -> Ok("🎊") 334 + ":balloon:" -> Ok("🎈") 335 + ":crystal_ball:" -> Ok("🔮") 336 + ":cd:" -> Ok("💿") 337 + ":dvd:" -> Ok("📀") 338 + ":floppy_disk:" -> Ok("💾") 339 + ":camera:" -> Ok("📷") 340 + ":video_camera:" -> Ok("📹") 341 + ":movie_camera:" -> Ok("🎥") 342 + ":computer:" -> Ok("💻") 343 + ":tv:" -> Ok("📺") 344 + ":iphone:" -> Ok("📱") 345 + ":phone:" -> Ok("☎️") 346 + ":telephone:" -> Ok("☎️") 347 + ":telephone_receiver:" -> Ok("📞") 348 + ":pager:" -> Ok("📟") 349 + ":fax:" -> Ok("📠") 350 + ":minidisc:" -> Ok("💽") 351 + ":vhs:" -> Ok("📼") 352 + ":sound:" -> Ok("🔉") 353 + ":speaker:" -> Ok("🔊") 354 + ":mute:" -> Ok("🔇") 355 + ":loudspeaker:" -> Ok("📢") 356 + ":mega:" -> Ok("📣") 357 + ":hourglass:" -> Ok("⌛") 358 + ":hourglass_flowing_sand:" -> Ok("⏳") 359 + ":alarm_clock:" -> Ok("⏰") 360 + ":watch:" -> Ok("⌚") 361 + ":radio:" -> Ok("📻") 362 + ":satellite:" -> Ok("📡") 363 + ":loop:" -> Ok("🔁") 364 + ":mag:" -> Ok("🔍") 365 + ":mag_right:" -> Ok("🔎") 366 + ":unlock:" -> Ok("🔓") 367 + ":lock:" -> Ok("🔒") 368 + ":lock_with_ink_pen:" -> Ok("🔏") 369 + ":closed_lock_with_key:" -> Ok("🔐") 370 + ":key:" -> Ok("🔑") 371 + ":bulb:" -> Ok("💡") 372 + ":flashlight:" -> Ok("🔦") 373 + ":high_brightness:" -> Ok("🔆") 374 + ":low_brightness:" -> Ok("🔅") 375 + ":electric_plug:" -> Ok("🔌") 376 + ":battery:" -> Ok("🔋") 377 + ":calling:" -> Ok("📲") 378 + ":email:" -> Ok("📧") 379 + ":mailbox:" -> Ok("📫") 380 + ":postbox:" -> Ok("📮") 381 + ":bath:" -> Ok("🛁") 382 + ":bathtub:" -> Ok("🛀") 383 + ":shower:" -> Ok("🚿") 384 + ":toilet:" -> Ok("🚽") 385 + ":wrench:" -> Ok("🔧") 386 + ":nut_and_bolt:" -> Ok("🔩") 387 + ":hammer:" -> Ok("🔨") 388 + ":seat:" -> Ok("💺") 389 + ":moneybag:" -> Ok("💰") 390 + ":yen:" -> Ok("💴") 391 + ":dollar:" -> Ok("💵") 392 + ":pound:" -> Ok("💷") 393 + ":euro:" -> Ok("💶") 394 + ":credit_card:" -> Ok("💳") 395 + ":money_with_wings:" -> Ok("💸") 396 + ":e-mail:" -> Ok("📧") 397 + ":inbox_tray:" -> Ok("📥") 398 + ":outbox_tray:" -> Ok("📤") 399 + ":envelope:" -> Ok("✉️") 400 + ":incoming_envelope:" -> Ok("📨") 401 + ":postal_horn:" -> Ok("📯") 402 + ":mailbox_closed:" -> Ok("📪") 403 + ":mailbox_with_mail:" -> Ok("📬") 404 + ":mailbox_with_no_mail:" -> Ok("📭") 405 + ":door:" -> Ok("🚪") 406 + ":smoking:" -> Ok("🚬") 407 + ":bomb:" -> Ok("💣") 408 + ":gun:" -> Ok("🔫") 409 + ":hocho:" -> Ok("🔪") 410 + ":pill:" -> Ok("💊") 411 + ":syringe:" -> Ok("💉") 412 + ":page_facing_up:" -> Ok("📄") 413 + ":page_with_curl:" -> Ok("📃") 414 + ":bookmark_tabs:" -> Ok("📑") 415 + ":bar_chart:" -> Ok("📊") 416 + ":chart_with_upwards_trend:" -> Ok("📈") 417 + ":chart_with_downwards_trend:" -> Ok("📉") 418 + ":scroll:" -> Ok("📜") 419 + ":clipboard:" -> Ok("📋") 420 + ":calendar:" -> Ok("📆") 421 + ":date:" -> Ok("📅") 422 + ":card_index:" -> Ok("📇") 423 + ":file_folder:" -> Ok("📁") 424 + ":open_file_folder:" -> Ok("📂") 425 + ":scissors:" -> Ok("✂️") 426 + ":pushpin:" -> Ok("📌") 427 + ":paperclip:" -> Ok("📎") 428 + ":black_nib:" -> Ok("✒️") 429 + ":pencil2:" -> Ok("✏️") 430 + ":straight_ruler:" -> Ok("📏") 431 + ":triangular_ruler:" -> Ok("📐") 432 + ":closed_book:" -> Ok("📕") 433 + ":green_book:" -> Ok("📗") 434 + ":blue_book:" -> Ok("📘") 435 + ":orange_book:" -> Ok("📙") 436 + ":notebook:" -> Ok("📓") 437 + ":notebook_with_decorative_cover:" -> Ok("📔") 438 + ":ledger:" -> Ok("📒") 439 + ":books:" -> Ok("📚") 440 + ":bookmark:" -> Ok("🔖") 441 + ":name_badge:" -> Ok("📛") 442 + ":microscope:" -> Ok("🔬") 443 + ":telescope:" -> Ok("🔭") 444 + ":newspaper:" -> Ok("📰") 445 + ":football:" -> Ok("🏈") 446 + ":basketball:" -> Ok("🏀") 447 + ":soccer:" -> Ok("⚽") 448 + ":baseball:" -> Ok("⚾") 449 + ":tennis:" -> Ok("🎾") 450 + ":8ball:" -> Ok("🎱") 451 + ":rugby_football:" -> Ok("🏉") 452 + ":bowling:" -> Ok("🎳") 453 + ":golf:" -> Ok("⛳") 454 + ":mountain_bicyclist:" -> Ok("🚵") 455 + ":bicyclist:" -> Ok("🚴") 456 + ":horse_racing:" -> Ok("🏇") 457 + ":snowboarder:" -> Ok("🏂") 458 + ":swimmer:" -> Ok("🏊") 459 + ":surfer:" -> Ok("🏄") 460 + ":ski:" -> Ok("🎿") 461 + ":spades:" -> Ok("♠️") 462 + ":hearts:" -> Ok("♥️") 463 + ":clubs:" -> Ok("♣️") 464 + ":diamonds:" -> Ok("♦️") 465 + ":gem:" -> Ok("💎") 466 + ":ring:" -> Ok("💍") 467 + ":trophy:" -> Ok("🏆") 468 + ":musical_score:" -> Ok("🎼") 469 + ":musical_keyboard:" -> Ok("🎹") 470 + ":violin:" -> Ok("🎻") 471 + ":space_invader:" -> Ok("👾") 472 + ":video_game:" -> Ok("🎮") 473 + ":black_joker:" -> Ok("🃏") 474 + ":flower_playing_cards:" -> Ok("🎴") 475 + ":game_die:" -> Ok("🎲") 476 + ":dart:" -> Ok("🎯") 477 + ":mahjong:" -> Ok("🀄") 478 + ":clapper:" -> Ok("🎬") 479 + ":memo:" -> Ok("📝") 480 + ":pencil:" -> Ok("📝") 481 + ":book:" -> Ok("📖") 482 + ":art:" -> Ok("🎨") 483 + ":microphone:" -> Ok("🎤") 484 + ":headphones:" -> Ok("🎧") 485 + ":trumpet:" -> Ok("🎺") 486 + ":saxophone:" -> Ok("🎷") 487 + ":guitar:" -> Ok("🎸") 488 + ":shoe:" -> Ok("👞") 489 + ":sandal:" -> Ok("👡") 490 + ":high_heel:" -> Ok("👠") 491 + ":lipstick:" -> Ok("💄") 492 + ":boot:" -> Ok("👢") 493 + ":shirt:" -> Ok("👕") 494 + ":tshirt:" -> Ok("👕") 495 + ":necktie:" -> Ok("👔") 496 + ":womans_clothes:" -> Ok("👚") 497 + ":dress:" -> Ok("👗") 498 + ":running_shirt_with_sash:" -> Ok("🎽") 499 + ":jeans:" -> Ok("👖") 500 + ":kimono:" -> Ok("👘") 501 + ":bikini:" -> Ok("👙") 502 + ":ribbon:" -> Ok("🎀") 503 + ":tophat:" -> Ok("🎩") 504 + ":crown:" -> Ok("👑") 505 + ":womans_hat:" -> Ok("👒") 506 + ":mans_shoe:" -> Ok("👞") 507 + ":closed_umbrella:" -> Ok("🌂") 508 + ":briefcase:" -> Ok("💼") 509 + ":handbag:" -> Ok("👜") 510 + ":pouch:" -> Ok("👝") 511 + ":purse:" -> Ok("👛") 512 + ":eyeglasses:" -> Ok("👓") 513 + ":fishing_pole_and_fish:" -> Ok("🎣") 514 + ":coffee:" -> Ok("☕") 515 + ":tea:" -> Ok("🍵") 516 + ":sake:" -> Ok("🍶") 517 + ":baby_bottle:" -> Ok("🍼") 518 + ":beer:" -> Ok("🍺") 519 + ":beers:" -> Ok("🍻") 520 + ":cocktail:" -> Ok("🍸") 521 + ":tropical_drink:" -> Ok("🍹") 522 + ":wine_glass:" -> Ok("🍷") 523 + ":fork_and_knife:" -> Ok("🍴") 524 + ":pizza:" -> Ok("🍕") 525 + ":hamburger:" -> Ok("🍔") 526 + ":fries:" -> Ok("🍟") 527 + ":poultry_leg:" -> Ok("🍗") 528 + ":meat_on_bone:" -> Ok("🍖") 529 + ":spaghetti:" -> Ok("🍝") 530 + ":curry:" -> Ok("🍛") 531 + ":fried_shrimp:" -> Ok("🍤") 532 + ":bento:" -> Ok("🍱") 533 + ":sushi:" -> Ok("🍣") 534 + ":fish_cake:" -> Ok("🍥") 535 + ":rice_ball:" -> Ok("🍙") 536 + ":rice_cracker:" -> Ok("🍘") 537 + ":rice:" -> Ok("🍚") 538 + ":ramen:" -> Ok("🍜") 539 + ":stew:" -> Ok("🍲") 540 + ":oden:" -> Ok("🍢") 541 + ":dango:" -> Ok("🍡") 542 + ":egg:" -> Ok("🍳") 543 + ":bread:" -> Ok("🍞") 544 + ":doughnut:" -> Ok("🍩") 545 + ":custard:" -> Ok("🍮") 546 + ":icecream:" -> Ok("🍨") 547 + ":ice_cream:" -> Ok("🍦") 548 + ":shaved_ice:" -> Ok("🍧") 549 + ":birthday:" -> Ok("🎂") 550 + ":cake:" -> Ok("🍰") 551 + ":cookie:" -> Ok("🍪") 552 + ":chocolate_bar:" -> Ok("🍫") 553 + ":candy:" -> Ok("🍬") 554 + ":lollipop:" -> Ok("🍭") 555 + ":honey_pot:" -> Ok("🍯") 556 + ":apple:" -> Ok("🍎") 557 + ":green_apple:" -> Ok("🍏") 558 + ":tangerine:" -> Ok("🍊") 559 + ":lemon:" -> Ok("🍋") 560 + ":cherries:" -> Ok("🍒") 561 + ":grapes:" -> Ok("🍇") 562 + ":watermelon:" -> Ok("🍉") 563 + ":strawberry:" -> Ok("🍓") 564 + ":peach:" -> Ok("🍑") 565 + ":melon:" -> Ok("🍈") 566 + ":banana:" -> Ok("🍌") 567 + ":pear:" -> Ok("🍐") 568 + ":pineapple:" -> Ok("🍍") 569 + ":sweet_potato:" -> Ok("🍠") 570 + ":eggplant:" -> Ok("🍆") 571 + ":tomato:" -> Ok("🍅") 572 + ":corn:" -> Ok("🌽") 573 + 574 + ":house:" -> Ok("🏠") 575 + ":house_with_garden:" -> Ok("🏡") 576 + ":school:" -> Ok("🏫") 577 + ":office:" -> Ok("🏢") 578 + ":post_office:" -> Ok("🏣") 579 + ":hospital:" -> Ok("🏥") 580 + ":bank:" -> Ok("🏦") 581 + ":convenience_store:" -> Ok("🏪") 582 + ":love_hotel:" -> Ok("🏩") 583 + ":hotel:" -> Ok("🏨") 584 + ":wedding:" -> Ok("💒") 585 + ":church:" -> Ok("⛪") 586 + ":department_store:" -> Ok("🏬") 587 + ":european_post_office:" -> Ok("🏤") 588 + ":city_sunrise:" -> Ok("🌇") 589 + ":city_sunset:" -> Ok("🌆") 590 + ":japanese_castle:" -> Ok("🏯") 591 + ":european_castle:" -> Ok("🏰") 592 + ":tent:" -> Ok("⛺") 593 + ":factory:" -> Ok("🏭") 594 + ":tokyo_tower:" -> Ok("🗼") 595 + ":japan:" -> Ok("🗾") 596 + ":mount_fuji:" -> Ok("🗻") 597 + ":sunrise_over_mountains:" -> Ok("🌄") 598 + ":sunrise:" -> Ok("🌅") 599 + ":stars:" -> Ok("🌠") 600 + ":statue_of_liberty:" -> Ok("🗽") 601 + ":bridge_at_night:" -> Ok("🌉") 602 + ":carousel_horse:" -> Ok("🎠") 603 + ":rainbow:" -> Ok("🌈") 604 + ":ferris_wheel:" -> Ok("🎡") 605 + ":fountain:" -> Ok("⛲") 606 + ":roller_coaster:" -> Ok("🎢") 607 + ":ship:" -> Ok("🚢") 608 + ":speedboat:" -> Ok("🚤") 609 + ":boat:" -> Ok("⛵") 610 + ":sailboat:" -> Ok("⛵") 611 + ":rowboat:" -> Ok("🚣") 612 + ":anchor:" -> Ok("⚓") 613 + ":rocket:" -> Ok("🚀") 614 + ":airplane:" -> Ok("✈️") 615 + ":helicopter:" -> Ok("🚁") 616 + ":steam_locomotive:" -> Ok("🚂") 617 + ":tram:" -> Ok("🚊") 618 + ":mountain_railway:" -> Ok("🚞") 619 + ":bike:" -> Ok("🚲") 620 + ":aerial_tramway:" -> Ok("🚡") 621 + ":suspension_railway:" -> Ok("🚟") 622 + ":mountain_cableway:" -> Ok("🚠") 623 + ":tractor:" -> Ok("🚜") 624 + ":blue_car:" -> Ok("🚙") 625 + ":oncoming_automobile:" -> Ok("🚘") 626 + ":car:" -> Ok("🚗") 627 + ":red_car:" -> Ok("🚗") 628 + ":taxi:" -> Ok("🚕") 629 + ":oncoming_taxi:" -> Ok("🚖") 630 + ":articulated_lorry:" -> Ok("🚛") 631 + ":bus:" -> Ok("🚌") 632 + ":oncoming_bus:" -> Ok("🚍") 633 + ":rotating_light:" -> Ok("🚨") 634 + ":police_car:" -> Ok("🚓") 635 + ":oncoming_police_car:" -> Ok("🚔") 636 + ":fire_engine:" -> Ok("🚒") 637 + ":ambulance:" -> Ok("🚑") 638 + ":minibus:" -> Ok("🚐") 639 + ":truck:" -> Ok("🚚") 640 + ":train:" -> Ok("🚋") 641 + ":station:" -> Ok("🚉") 642 + ":train2:" -> Ok("🚆") 643 + ":bullettrain_front:" -> Ok("🚅") 644 + ":bullettrain_side:" -> Ok("🚄") 645 + ":light_rail:" -> Ok("🚈") 646 + ":monorail:" -> Ok("🚝") 647 + ":railway_car:" -> Ok("🚃") 648 + ":trolleybus:" -> Ok("🚎") 649 + ":ticket:" -> Ok("🎫") 650 + ":fuelpump:" -> Ok("⛽") 651 + ":vertical_traffic_light:" -> Ok("🚦") 652 + ":traffic_light:" -> Ok("🚥") 653 + ":warning:" -> Ok("⚠️") 654 + ":construction:" -> Ok("🚧") 655 + ":beginner:" -> Ok("🔰") 656 + ":atm:" -> Ok("🏧") 657 + ":slot_machine:" -> Ok("🎰") 658 + ":busstop:" -> Ok("🚏") 659 + ":barber:" -> Ok("💈") 660 + ":hotsprings:" -> Ok("♨️") 661 + ":checkered_flag:" -> Ok("🏁") 662 + ":crossed_flags:" -> Ok("🎌") 663 + ":izakaya_lantern:" -> Ok("🏮") 664 + ":moyai:" -> Ok("🗿") 665 + ":circus_tent:" -> Ok("🎪") 666 + ":performing_arts:" -> Ok("🎭") 667 + ":round_pushpin:" -> Ok("📍") 668 + ":triangular_flag_on_post:" -> Ok("🚩") 669 + ":jp:" -> Ok("🇯🇵") 670 + ":kr:" -> Ok("🇰🇷") 671 + ":cn:" -> Ok("🇨🇳") 672 + ":us:" -> Ok("🇺🇸") 673 + ":fr:" -> Ok("🇫🇷") 674 + ":es:" -> Ok("🇪🇸") 675 + ":it:" -> Ok("🇮🇹") 676 + ":ru:" -> Ok("🇷🇺") 677 + ":gb:" -> Ok("🇬🇧") 678 + ":uk:" -> Ok("🇬🇧") 679 + ":de:" -> Ok("🇩🇪") 680 + 681 + ":one:" -> Ok("1️⃣") 682 + ":two:" -> Ok("2️⃣") 683 + ":three:" -> Ok("3️⃣") 684 + ":four:" -> Ok("4️⃣") 685 + ":five:" -> Ok("5️⃣") 686 + ":six:" -> Ok("6️⃣") 687 + ":seven:" -> Ok("7️⃣") 688 + ":eight:" -> Ok("8️⃣") 689 + ":nine:" -> Ok("9️⃣") 690 + ":keycap_ten:" -> Ok("🔟") 691 + ":1234:" -> Ok("🔢") 692 + ":zero:" -> Ok("0️⃣") 693 + ":hash:" -> Ok("#️⃣") 694 + ":symbols:" -> Ok("🔣") 695 + ":arrow_backward:" -> Ok("◀️") 696 + ":arrow_down:" -> Ok("⬇️") 697 + ":arrow_forward:" -> Ok("▶️") 698 + ":arrow_left:" -> Ok("⬅️") 699 + ":capital_abcd:" -> Ok("🔠") 700 + ":abcd:" -> Ok("🔡") 701 + ":abc:" -> Ok("🔤") 702 + ":arrow_lower_left:" -> Ok("↙️") 703 + ":arrow_lower_right:" -> Ok("↘️") 704 + ":arrow_right:" -> Ok("➡️") 705 + ":arrow_up:" -> Ok("⬆️") 706 + ":arrow_upper_left:" -> Ok("↖️") 707 + ":arrow_upper_right:" -> Ok("↗️") 708 + ":arrow_double_down:" -> Ok("⤵️") 709 + ":arrow_double_up:" -> Ok("⤴️") 710 + ":arrow_down_small:" -> Ok("🔽") 711 + ":arrow_heading_down:" -> Ok("⤵️") 712 + ":arrow_heading_up:" -> Ok("⤴️") 713 + ":leftwards_arrow_with_hook:" -> Ok("↩️") 714 + ":arrow_right_hook:" -> Ok("↪️") 715 + ":left_right_arrow:" -> Ok("↔️") 716 + ":arrow_up_down:" -> Ok("↕️") 717 + ":arrow_up_small:" -> Ok("🔼") 718 + ":arrows_clockwise:" -> Ok("🔃") 719 + ":arrows_counterclockwise:" -> Ok("🔄") 720 + ":rewind:" -> Ok("⏪") 721 + ":fast_forward:" -> Ok("⏩") 722 + ":information_source:" -> Ok("i️") 723 + ":ok:" -> Ok("🆗") 724 + ":twisted_rightwards_arrows:" -> Ok("🔀") 725 + ":repeat:" -> Ok("🔁") 726 + ":repeat_one:" -> Ok("🔂") 727 + ":new:" -> Ok("🆕") 728 + ":top:" -> Ok("🔝") 729 + ":up:" -> Ok("🆙") 730 + ":cool:" -> Ok("🆒") 731 + ":free:" -> Ok("🆓") 732 + ":ng:" -> Ok("🆖") 733 + ":cinema:" -> Ok("🎦") 734 + ":koko:" -> Ok("ココ") 735 + ":signal_strength:" -> Ok("📶") 736 + ":u5272:" -> Ok("割") 737 + ":u5408:" -> Ok("合") 738 + ":u55b6:" -> Ok("営") 739 + ":u6307:" -> Ok("指") 740 + ":u6708:" -> Ok("月️") 741 + ":u6709:" -> Ok("有") 742 + ":u6e80:" -> Ok("満") 743 + ":u7121:" -> Ok("無") 744 + ":u7533:" -> Ok("申") 745 + ":u7a7a:" -> Ok("空") 746 + ":u7981:" -> Ok("禁") 747 + ":sa:" -> Ok("サ️") 748 + ":restroom:" -> Ok("🚻") 749 + ":mens:" -> Ok("🚹") 750 + ":womens:" -> Ok("🚺") 751 + ":baby_symbol:" -> Ok("🚼") 752 + ":no_smoking:" -> Ok("🚭") 753 + ":parking:" -> Ok("🅿️") 754 + ":wheelchair:" -> Ok("♿") 755 + ":metro:" -> Ok("🚇") 756 + ":baggage_claim:" -> Ok("🛄") 757 + ":accept:" -> Ok("可") 758 + ":wc:" -> Ok("🚾") 759 + ":potable_water:" -> Ok("🚰") 760 + ":put_litter_in_its_place:" -> Ok("🚮") 761 + ":secret:" -> Ok("秘️") 762 + ":congratulations:" -> Ok("祝️") 763 + ":m:" -> Ok("M️") 764 + ":passport_control:" -> Ok("🛂") 765 + ":left_luggage:" -> Ok("🛅") 766 + ":customs:" -> Ok("🛃") 767 + ":ideograph_advantage:" -> Ok("得") 768 + ":cl:" -> Ok("🆑") 769 + ":sos:" -> Ok("🆘") 770 + ":id:" -> Ok("🆔") 771 + ":no_entry_sign:" -> Ok("🚫") 772 + ":underage:" -> Ok("🔞") 773 + ":no_mobile_phones:" -> Ok("📵") 774 + ":do_not_litter:" -> Ok("🚯") 775 + ":non-potable_water:" -> Ok("🚱") 776 + ":no_bicycles:" -> Ok("🚳") 777 + ":no_pedestrians:" -> Ok("🚷") 778 + ":children_crossing:" -> Ok("🚸") 779 + ":no_entry:" -> Ok("⛔") 780 + ":eight_spoked_asterisk:" -> Ok("✳️") 781 + ":eight_pointed_black_star:" -> Ok("✴️") 782 + ":heart_decoration:" -> Ok("💟") 783 + ":vs:" -> Ok("🆚") 784 + ":vibration_mode:" -> Ok("📳") 785 + ":mobile_phone_off:" -> Ok("📴") 786 + ":chart:" -> Ok("💹") 787 + ":currency_exchange:" -> Ok("💱") 788 + ":aries:" -> Ok("♈") 789 + ":taurus:" -> Ok("♉") 790 + ":gemini:" -> Ok("♊") 791 + ":cancer:" -> Ok("♋") 792 + ":leo:" -> Ok("♌") 793 + ":virgo:" -> Ok("♍") 794 + ":libra:" -> Ok("♎") 795 + ":scorpius:" -> Ok("♏") 796 + ":sagittarius:" -> Ok("♐") 797 + ":capricorn:" -> Ok("♑") 798 + ":aquarius:" -> Ok("♒") 799 + ":pisces:" -> Ok("♓") 800 + ":ophiuchus:" -> Ok("⛎") 801 + ":six_pointed_star:" -> Ok("🔯") 802 + ":negative_squared_cross_mark:" -> Ok("❎") 803 + ":a:" -> Ok("🅰️") 804 + ":b:" -> Ok("🅱️") 805 + ":ab:" -> Ok("🆎") 806 + ":o2:" -> Ok("🅾️") 807 + ":diamond_shape_with_a_dot_inside:" -> Ok("💠") 808 + ":recycle:" -> Ok("♻️") 809 + ":end:" -> Ok("🔚") 810 + ":on:" -> Ok("🔛") 811 + ":soon:" -> Ok("🔜") 812 + ":clock1:" -> Ok("🕐") 813 + ":clock130:" -> Ok("🕜") 814 + ":clock10:" -> Ok("🕙") 815 + ":clock1030:" -> Ok("🕥") 816 + ":clock11:" -> Ok("🕚") 817 + ":clock1130:" -> Ok("🕦") 818 + ":clock12:" -> Ok("🕛") 819 + ":clock1230:" -> Ok("🕧") 820 + ":clock2:" -> Ok("🕑") 821 + ":clock230:" -> Ok("🕝") 822 + ":clock3:" -> Ok("🕒") 823 + ":clock330:" -> Ok("🕞") 824 + ":clock4:" -> Ok("🕓") 825 + ":clock430:" -> Ok("🕟") 826 + ":clock5:" -> Ok("🕔") 827 + ":clock530:" -> Ok("🕠") 828 + ":clock6:" -> Ok("🕕") 829 + ":clock630:" -> Ok("🕡") 830 + ":clock7:" -> Ok("🕖") 831 + ":clock730:" -> Ok("🕢") 832 + ":clock8:" -> Ok("🕗") 833 + ":clock830:" -> Ok("🕣") 834 + ":clock9:" -> Ok("🕘") 835 + ":clock930:" -> Ok("🕤") 836 + ":heavy_dollar_sign:" -> Ok("💲") 837 + ":copyright:" -> Ok("©️") 838 + ":registered:" -> Ok("®️") 839 + ":tm:" -> Ok("TM️") 840 + ":x:" -> Ok("❌") 841 + ":heavy_exclamation_mark:" -> Ok("❗") 842 + ":bangbang:" -> Ok("!!️") 843 + ":interrobang:" -> Ok("!?️") 844 + ":o:" -> Ok("⭕") 845 + ":heavy_multiplication_x:" -> Ok("✖️") 846 + ":heavy_plus_sign:" -> Ok("➕") 847 + ":heavy_minus_sign:" -> Ok("➖") 848 + ":heavy_division_sign:" -> Ok("➗") 849 + ":white_flower:" -> Ok("💮") 850 + ":100:" -> Ok("💯") 851 + ":heavy_check_mark:" -> Ok("✔️") 852 + ":ballot_box_with_check:" -> Ok("☑️") 853 + ":radio_button:" -> Ok("🔘") 854 + ":link:" -> Ok("🔗") 855 + ":curly_loop:" -> Ok("➰") 856 + ":wavy_dash:" -> Ok("〰️") 857 + ":part_alternation_mark:" -> Ok("〽️") 858 + ":trident:" -> Ok("🔱") 859 + ":flag_white:" -> Ok("🏳") 860 + ":flag_black:" -> Ok("🏴") 861 + ":pirate_flag:" -> Ok("🏴‍☠️") 862 + 863 + ":flag_ac:" -> Ok("🇦🇨") 864 + ":flag_ad:" -> Ok("🇦🇩") 865 + ":flag_ae:" -> Ok("🇦🇪") 866 + ":flag_af:" -> Ok("🇦🇫") 867 + ":flag_ag:" -> Ok("🇦🇬") 868 + ":flag_ai:" -> Ok("🇦🇮") 869 + ":flag_al:" -> Ok("🇦🇱") 870 + ":flag_am:" -> Ok("🇦🇲") 871 + ":flag_ao:" -> Ok("🇦🇴") 872 + ":flag_aq:" -> Ok("🇦🇶") 873 + ":flag_ar:" -> Ok("🇦🇷") 874 + ":flag_as:" -> Ok("🇦🇸") 875 + ":flag_at:" -> Ok("🇦🇹") 876 + ":flag_au:" -> Ok("🇦🇺") 877 + ":flag_aw:" -> Ok("🇦🇼") 878 + ":flag_ax:" -> Ok("🇦🇽") 879 + ":flag_az:" -> Ok("🇦🇿") 880 + 881 + ":flag_ba:" -> Ok("🇧🇦") 882 + ":flag_bb:" -> Ok("🇧🇧") 883 + ":flag_bd:" -> Ok("🇧🇩") 884 + ":flag_be:" -> Ok("🇧🇪") 885 + ":flag_bf:" -> Ok("🇧🇫") 886 + ":flag_bg:" -> Ok("🇧🇬") 887 + ":flag_bh:" -> Ok("🇧🇭") 888 + ":flag_bi:" -> Ok("🇧🇮") 889 + ":flag_bj:" -> Ok("🇧🇯") 890 + ":flag_bl:" -> Ok("🇧🇱") 891 + ":flag_bm:" -> Ok("🇧🇲") 892 + ":flag_bn:" -> Ok("🇧🇳") 893 + ":flag_bo:" -> Ok("🇧🇴") 894 + ":flag_bq:" -> Ok("🇧🇶") 895 + ":flag_br:" -> Ok("🇧🇷") 896 + ":flag_bs:" -> Ok("🇧🇸") 897 + ":flag_bt:" -> Ok("🇧🇹") 898 + ":flag_bv:" -> Ok("🇧🇻") 899 + ":flag_bw:" -> Ok("🇧🇼") 900 + ":flag_by:" -> Ok("🇧🇾") 901 + ":flag_bz:" -> Ok("🇧🇿") 902 + 903 + ":flag_ca:" -> Ok("🇨🇦") 904 + ":flag_cc:" -> Ok("🇨🇨") 905 + ":flag_cd:" -> Ok("🇨🇩") 906 + ":flag_cf:" -> Ok("🇨🇫") 907 + ":flag_cg:" -> Ok("🇨🇬") 908 + ":flag_ch:" -> Ok("🇨🇭") 909 + ":flag_ci:" -> Ok("🇨🇮") 910 + ":flag_ck:" -> Ok("🇨🇰") 911 + ":flag_cl:" -> Ok("🇨🇱") 912 + ":flag_cm:" -> Ok("🇨🇲") 913 + ":flag_cn:" -> Ok("🇨🇳") 914 + ":flag_co:" -> Ok("🇨🇴") 915 + ":flag_cp:" -> Ok("🇨🇵") 916 + ":flag_cr:" -> Ok("🇨🇷") 917 + ":flag_cu:" -> Ok("🇨🇺") 918 + ":flag_cv:" -> Ok("🇨🇻") 919 + ":flag_cw:" -> Ok("🇨🇼") 920 + ":flag_cx:" -> Ok("🇨🇽") 921 + ":flag_cy:" -> Ok("🇨🇾") 922 + ":flag_cz:" -> Ok("🇨🇿") 923 + 924 + ":flag_de:" -> Ok("🇩🇪") 925 + ":flag_dg:" -> Ok("🇩🇬") 926 + ":flag_dj:" -> Ok("🇩🇯") 927 + ":flag_dk:" -> Ok("🇩🇰") 928 + ":flag_dm:" -> Ok("🇩🇲") 929 + ":flag_do:" -> Ok("🇩🇴") 930 + ":flag_dz:" -> Ok("🇩🇿") 931 + 932 + ":flag_ea:" -> Ok("🇪🇦") 933 + ":flag_ec:" -> Ok("🇪🇨") 934 + ":flag_ee:" -> Ok("🇪🇪") 935 + ":flag_eg:" -> Ok("🇪🇬") 936 + ":flag_eh:" -> Ok("🇪🇭") 937 + ":flag_er:" -> Ok("🇪🇷") 938 + ":flag_es:" -> Ok("🇪🇸") 939 + ":flag_et:" -> Ok("🇪🇹") 940 + ":flag_eu:" -> Ok("🇪🇺") 941 + 942 + ":flag_fi:" -> Ok("🇫🇮") 943 + ":flag_fj:" -> Ok("🇫🇯") 944 + ":flag_fk:" -> Ok("🇫🇰") 945 + ":flag_fm:" -> Ok("🇫🇲") 946 + ":flag_fo:" -> Ok("🇫🇴") 947 + ":flag_fr:" -> Ok("🇫🇷") 948 + 949 + ":flag_ga:" -> Ok("🇬🇦") 950 + ":flag_gb:" -> Ok("🇬🇧") 951 + ":flag_gd:" -> Ok("🇬🇩") 952 + ":flag_ge:" -> Ok("🇬🇪") 953 + ":flag_gf:" -> Ok("🇬🇫") 954 + ":flag_gg:" -> Ok("🇬🇬") 955 + ":flag_gh:" -> Ok("🇬🇭") 956 + ":flag_gi:" -> Ok("🇬🇮") 957 + ":flag_gl:" -> Ok("🇬🇱") 958 + ":flag_gm:" -> Ok("🇬🇲") 959 + ":flag_gn:" -> Ok("🇬🇳") 960 + ":flag_gp:" -> Ok("🇬🇵") 961 + ":flag_gq:" -> Ok("🇬🇶") 962 + ":flag_gr:" -> Ok("🇬🇷") 963 + ":flag_gs:" -> Ok("🇬🇸") 964 + ":flag_gt:" -> Ok("🇬🇹") 965 + ":flag_gu:" -> Ok("🇬🇺") 966 + ":flag_gw:" -> Ok("🇬🇼") 967 + ":flag_gy:" -> Ok("🇬🇾") 968 + 969 + ":flag_hk:" -> Ok("🇭🇰") 970 + ":flag_hm:" -> Ok("🇭🇲") 971 + ":flag_hn:" -> Ok("🇭🇳") 972 + ":flag_hr:" -> Ok("🇭🇷") 973 + ":flag_ht:" -> Ok("🇭🇹") 974 + ":flag_hu:" -> Ok("🇭🇺") 975 + 976 + ":flag_ic:" -> Ok("🇮🇨") 977 + ":flag_id:" -> Ok("🇮🇩") 978 + ":flag_ie:" -> Ok("🇮🇪") 979 + ":flag_il:" -> Ok("🇮🇱") 980 + ":flag_im:" -> Ok("🇮🇲") 981 + ":flag_in:" -> Ok("🇮🇳") 982 + ":flag_io:" -> Ok("🇮🇴") 983 + ":flag_iq:" -> Ok("🇮🇶") 984 + ":flag_ir:" -> Ok("🇮🇷") 985 + ":flag_is:" -> Ok("🇮🇸") 986 + ":flag_it:" -> Ok("🇮🇹") 987 + 988 + ":flag_je:" -> Ok("🇯🇪") 989 + ":flag_jm:" -> Ok("🇯🇲") 990 + ":flag_jo:" -> Ok("🇯🇴") 991 + ":flag_jp:" -> Ok("🇯🇵") 992 + 993 + ":flag_ke:" -> Ok("🇰🇪") 994 + ":flag_kg:" -> Ok("🇰🇬") 995 + ":flag_kh:" -> Ok("🇰🇭") 996 + ":flag_ki:" -> Ok("🇰🇮") 997 + ":flag_km:" -> Ok("🇰🇲") 998 + ":flag_kn:" -> Ok("🇰🇳") 999 + ":flag_kp:" -> Ok("🇰🇵") 1000 + ":flag_kr:" -> Ok("🇰🇷") 1001 + ":flag_kw:" -> Ok("🇰🇼") 1002 + ":flag_ky:" -> Ok("🇰🇾") 1003 + ":flag_kz:" -> Ok("🇰🇿") 1004 + 1005 + ":flag_la:" -> Ok("🇱🇦") 1006 + ":flag_lb:" -> Ok("🇱🇧") 1007 + ":flag_lc:" -> Ok("🇱🇨") 1008 + ":flag_li:" -> Ok("🇱🇮") 1009 + ":flag_lk:" -> Ok("🇱🇰") 1010 + ":flag_lr:" -> Ok("🇱🇷") 1011 + ":flag_ls:" -> Ok("🇱🇸") 1012 + ":flag_lt:" -> Ok("🇱🇹") 1013 + ":flag_lu:" -> Ok("🇱🇺") 1014 + ":flag_lv:" -> Ok("🇱🇻") 1015 + ":flag_ly:" -> Ok("🇱🇾") 1016 + 1017 + ":flag_ma:" -> Ok("🇲🇦") 1018 + ":flag_mc:" -> Ok("🇲🇨") 1019 + ":flag_md:" -> Ok("🇲🇩") 1020 + ":flag_me:" -> Ok("🇲🇪") 1021 + ":flag_mf:" -> Ok("🇲🇫") 1022 + ":flag_mg:" -> Ok("🇲🇬") 1023 + ":flag_mh:" -> Ok("🇲🇭") 1024 + ":flag_mk:" -> Ok("🇲🇰") 1025 + ":flag_ml:" -> Ok("🇲🇱") 1026 + ":flag_mm:" -> Ok("🇲🇲") 1027 + ":flag_mn:" -> Ok("🇲🇳") 1028 + ":flag_mo:" -> Ok("🇲🇴") 1029 + ":flag_mp:" -> Ok("🇲🇵") 1030 + ":flag_mq:" -> Ok("🇲🇶") 1031 + ":flag_mr:" -> Ok("🇲🇷") 1032 + ":flag_ms:" -> Ok("🇲🇸") 1033 + ":flag_mt:" -> Ok("🇲🇹") 1034 + ":flag_mu:" -> Ok("🇲🇺") 1035 + ":flag_mv:" -> Ok("🇲🇻") 1036 + ":flag_mw:" -> Ok("🇲🇼") 1037 + ":flag_mx:" -> Ok("🇲🇽") 1038 + ":flag_my:" -> Ok("🇲🇾") 1039 + ":flag_mz:" -> Ok("🇲🇿") 1040 + 1041 + ":flag_na:" -> Ok("🇳🇦") 1042 + ":flag_nc:" -> Ok("🇳🇨") 1043 + ":flag_ne:" -> Ok("🇳🇪") 1044 + ":flag_nf:" -> Ok("🇳🇫") 1045 + ":flag_ng:" -> Ok("🇳🇬") 1046 + ":flag_ni:" -> Ok("🇳🇮") 1047 + ":flag_nl:" -> Ok("🇳🇱") 1048 + ":flag_no:" -> Ok("🇳🇴") 1049 + ":flag_np:" -> Ok("🇳🇵") 1050 + ":flag_nr:" -> Ok("🇳🇷") 1051 + ":flag_nu:" -> Ok("🇳🇺") 1052 + ":flag_nz:" -> Ok("🇳🇿") 1053 + 1054 + ":flag_om:" -> Ok("🇴🇲") 1055 + 1056 + ":flag_pa:" -> Ok("🇵🇦") 1057 + ":flag_pe:" -> Ok("🇵🇪") 1058 + ":flag_pf:" -> Ok("🇵🇫") 1059 + ":flag_pg:" -> Ok("🇵🇬") 1060 + ":flag_ph:" -> Ok("🇵🇭") 1061 + ":flag_pk:" -> Ok("🇵🇰") 1062 + ":flag_pl:" -> Ok("🇵🇱") 1063 + ":flag_pm:" -> Ok("🇵🇲") 1064 + ":flag_pn:" -> Ok("🇵🇳") 1065 + ":flag_pr:" -> Ok("🇵🇷") 1066 + ":flag_ps:" -> Ok("🇵🇸") 1067 + ":flag_pt:" -> Ok("🇵🇹") 1068 + ":flag_pw:" -> Ok("🇵🇼") 1069 + ":flag_py:" -> Ok("🇵🇾") 1070 + 1071 + ":flag_qa:" -> Ok("🇶🇦") 1072 + 1073 + ":flag_re:" -> Ok("🇷🇪") 1074 + ":flag_ro:" -> Ok("🇷🇴") 1075 + ":flag_rs:" -> Ok("🇷🇸") 1076 + ":flag_ru:" -> Ok("🇷🇺") 1077 + ":flag_rw:" -> Ok("🇷🇼") 1078 + 1079 + ":flag_sa:" -> Ok("🇸🇦") 1080 + ":flag_sb:" -> Ok("🇸🇧") 1081 + ":flag_sc:" -> Ok("🇸🇨") 1082 + ":flag_sd:" -> Ok("🇸🇩") 1083 + ":flag_se:" -> Ok("🇸🇪") 1084 + ":flag_sg:" -> Ok("🇸🇬") 1085 + ":flag_sh:" -> Ok("🇸🇭") 1086 + ":flag_si:" -> Ok("🇸🇮") 1087 + ":flag_sj:" -> Ok("🇸🇯") 1088 + ":flag_sk:" -> Ok("🇸🇰") 1089 + ":flag_sl:" -> Ok("🇸🇱") 1090 + ":flag_sm:" -> Ok("🇸🇲") 1091 + ":flag_sn:" -> Ok("🇸🇳") 1092 + ":flag_so:" -> Ok("🇸🇴") 1093 + ":flag_sr:" -> Ok("🇸🇷") 1094 + ":flag_ss:" -> Ok("🇸🇸") 1095 + ":flag_st:" -> Ok("🇸🇹") 1096 + ":flag_sv:" -> Ok("🇸🇻") 1097 + ":flag_sx:" -> Ok("🇸🇽") 1098 + ":flag_sy:" -> Ok("🇸🇾") 1099 + ":flag_sz:" -> Ok("🇸🇿") 1100 + 1101 + ":flag_ta:" -> Ok("🇹🇦") 1102 + ":flag_tc:" -> Ok("🇹🇨") 1103 + ":flag_td:" -> Ok("🇹🇩") 1104 + ":flag_tf:" -> Ok("🇹🇫") 1105 + ":flag_tg:" -> Ok("🇹🇬") 1106 + ":flag_th:" -> Ok("🇹🇭") 1107 + ":flag_tj:" -> Ok("🇹🇯") 1108 + ":flag_tk:" -> Ok("🇹🇰") 1109 + ":flag_tl:" -> Ok("🇹🇱") 1110 + ":flag_tm:" -> Ok("🇹🇲") 1111 + ":flag_tn:" -> Ok("🇹🇳") 1112 + ":flag_to:" -> Ok("🇹🇴") 1113 + ":flag_tr:" -> Ok("🇹🇷") 1114 + ":flag_tt:" -> Ok("🇹🇹") 1115 + ":flag_tv:" -> Ok("🇹🇻") 1116 + ":flag_tw:" -> Ok("🇹🇼") 1117 + ":flag_tz:" -> Ok("🇹🇿") 1118 + 1119 + ":flag_ua:" -> Ok("🇺🇦") 1120 + ":flag_ug:" -> Ok("🇺🇬") 1121 + ":flag_um:" -> Ok("🇺🇲") 1122 + ":flag_us:" -> Ok("🇺🇸") 1123 + ":flag_uy:" -> Ok("🇺🇾") 1124 + ":flag_uz:" -> Ok("🇺🇿") 1125 + 1126 + ":flag_va:" -> Ok("🇻🇦") 1127 + ":flag_vc:" -> Ok("🇻🇨") 1128 + ":flag_ve:" -> Ok("🇻🇪") 1129 + ":flag_vg:" -> Ok("🇻🇬") 1130 + ":flag_vi:" -> Ok("🇻🇮") 1131 + ":flag_vn:" -> Ok("🇻🇳") 1132 + ":flag_vu:" -> Ok("🇻🇺") 1133 + 1134 + ":flag_wf:" -> Ok("🇼🇫") 1135 + ":flag_ws:" -> Ok("🇼🇸") 1136 + 1137 + ":flag_xk:" -> Ok("🇽🇰") 1138 + 1139 + ":flag_ye:" -> Ok("🇾🇪") 1140 + ":flag_yt:" -> Ok("🇾🇹") 1141 + 1142 + ":flag_za:" -> Ok("🇿🇦") 1143 + ":flag_zm:" -> Ok("🇿🇲") 1144 + ":flag_zw:" -> Ok("🇿🇼") 1145 + _ -> Error(Nil) 1146 + } 1147 + }
+9
server/src/server/reaction_role.gleam
··· 1 + pub type ReactionRole { 2 + ReactionRole( 3 + guild_id: String, 4 + channel_id: String, 5 + message_id: String, 6 + emote: String, 7 + target_role_id: String, 8 + ) 9 + }
+82
server/src/server/reaction_role/database.gleam
··· 1 + import gleam/erlang/process 2 + import gleam/list 3 + import gleam/result 4 + import pog 5 + import server/reaction_role 6 + import server/reaction_role/sql 7 + 8 + /// creates the table if it doesnt exist yet 9 + /// 10 + pub fn create( 11 + pool_name: process.Name(pog.Message), 12 + ) -> Result(pog.Returned(Nil), pog.QueryError) { 13 + let connection = pog.named_connection(pool_name) 14 + sql.create_table(connection) 15 + } 16 + 17 + /// insert a new `ReactionRole` into the reaction_role table 18 + /// 19 + pub fn insert( 20 + pool_name pool_name: process.Name(pog.Message), 21 + role reaction_role: reaction_role.ReactionRole, 22 + ) -> Result(pog.Returned(Nil), pog.QueryError) { 23 + let reaction_role.ReactionRole( 24 + guild_id:, 25 + channel_id:, 26 + message_id:, 27 + emote:, 28 + target_role_id:, 29 + ) = reaction_role 30 + 31 + let connection = pog.named_connection(pool_name) 32 + 33 + sql.insert( 34 + connection, 35 + guild_id, 36 + channel_id, 37 + message_id, 38 + emote, 39 + target_role_id, 40 + ) 41 + } 42 + 43 + pub type MessageRef { 44 + MessageRef(guild_id: String, channel_id: String, message_id: String) 45 + } 46 + 47 + /// find specific `ReactionRole`'s based on their associated `MessageRef` 48 + /// 49 + pub fn find( 50 + pool_name pool_name: process.Name(pog.Message), 51 + find find: MessageRef, 52 + ) { 53 + let MessageRef(guild_id:, channel_id:, message_id:) = find 54 + 55 + let connection = pog.named_connection(pool_name) 56 + 57 + use returned <- result.try(sql.find( 58 + connection, 59 + guild_id, 60 + channel_id, 61 + message_id, 62 + )) 63 + 64 + returned.rows 65 + |> list.map(find_row_to_reaction_role) 66 + |> Ok 67 + } 68 + 69 + fn find_row_to_reaction_role( 70 + find_row: sql.FindRow, 71 + ) -> reaction_role.ReactionRole { 72 + let sql.FindRow(guild_id:, channel_id:, message_id:, emote:, target_role_id:) = 73 + find_row 74 + 75 + reaction_role.ReactionRole( 76 + guild_id:, 77 + channel_id:, 78 + message_id:, 79 + emote:, 80 + target_role_id:, 81 + ) 82 + }
+127
server/src/server/reaction_role/sql.gleam
··· 1 + //// This module contains the code to run the sql queries defined in 2 + //// `./src/server/reaction_role/sql`. 3 + //// > 🐿️ This module was generated automatically using v4.6.0 of 4 + //// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 5 + //// 6 + 7 + import gleam/dynamic/decode 8 + import pog 9 + 10 + /// creates reaction_roles table 11 + /// 12 + /// 13 + /// > 🐿️ This function was generated automatically using v4.6.0 of 14 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 15 + /// 16 + pub fn create_table( 17 + db: pog.Connection, 18 + ) -> Result(pog.Returned(Nil), pog.QueryError) { 19 + let decoder = decode.map(decode.dynamic, fn(_) { Nil }) 20 + 21 + "-- creates reaction_roles table 22 + -- 23 + create table if not exists reaction_roles ( 24 + guild_id text not null, 25 + channel_id text not null, 26 + message_id text not null, 27 + emote text not null, 28 + target_role_id text not null 29 + ) 30 + " 31 + |> pog.query 32 + |> pog.returning(decoder) 33 + |> pog.execute(db) 34 + } 35 + 36 + /// A row you get from running the `find` query 37 + /// defined in `./src/server/reaction_role/sql/find.sql`. 38 + /// 39 + /// > 🐿️ This type definition was generated automatically using v4.6.0 of the 40 + /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 41 + /// 42 + pub type FindRow { 43 + FindRow( 44 + guild_id: String, 45 + channel_id: String, 46 + message_id: String, 47 + emote: String, 48 + target_role_id: String, 49 + ) 50 + } 51 + 52 + /// finds an entry using guild_id, channel_id and message_id 53 + /// 54 + /// 55 + /// > 🐿️ This function was generated automatically using v4.6.0 of 56 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 57 + /// 58 + pub fn find( 59 + db: pog.Connection, 60 + arg_1: String, 61 + arg_2: String, 62 + arg_3: String, 63 + ) -> Result(pog.Returned(FindRow), pog.QueryError) { 64 + let decoder = { 65 + use guild_id <- decode.field(0, decode.string) 66 + use channel_id <- decode.field(1, decode.string) 67 + use message_id <- decode.field(2, decode.string) 68 + use emote <- decode.field(3, decode.string) 69 + use target_role_id <- decode.field(4, decode.string) 70 + decode.success(FindRow( 71 + guild_id:, 72 + channel_id:, 73 + message_id:, 74 + emote:, 75 + target_role_id:, 76 + )) 77 + } 78 + 79 + "-- finds an entry using guild_id, channel_id and message_id 80 + -- 81 + select 82 + * 83 + from 84 + reaction_roles 85 + where 86 + guild_id = $1 and channel_id = $2 and message_id = $3 87 + " 88 + |> pog.query 89 + |> pog.parameter(pog.text(arg_1)) 90 + |> pog.parameter(pog.text(arg_2)) 91 + |> pog.parameter(pog.text(arg_3)) 92 + |> pog.returning(decoder) 93 + |> pog.execute(db) 94 + } 95 + 96 + /// insert a new reaction_role record into the database 97 + /// 98 + /// 99 + /// > 🐿️ This function was generated automatically using v4.6.0 of 100 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 101 + /// 102 + pub fn insert( 103 + db: pog.Connection, 104 + arg_1: String, 105 + arg_2: String, 106 + arg_3: String, 107 + arg_4: String, 108 + arg_5: String, 109 + ) -> Result(pog.Returned(Nil), pog.QueryError) { 110 + let decoder = decode.map(decode.dynamic, fn(_) { Nil }) 111 + 112 + "-- insert a new reaction_role record into the database 113 + -- 114 + insert into reaction_roles 115 + (guild_id, channel_id, message_id, emote, target_role_id) 116 + values 117 + ($1, $2, $3, $4, $5) 118 + " 119 + |> pog.query 120 + |> pog.parameter(pog.text(arg_1)) 121 + |> pog.parameter(pog.text(arg_2)) 122 + |> pog.parameter(pog.text(arg_3)) 123 + |> pog.parameter(pog.text(arg_4)) 124 + |> pog.parameter(pog.text(arg_5)) 125 + |> pog.returning(decoder) 126 + |> pog.execute(db) 127 + }
+9
server/src/server/reaction_role/sql/create_table.sql
··· 1 + -- creates reaction_roles table 2 + -- 3 + create table if not exists reaction_roles ( 4 + guild_id text not null, 5 + channel_id text not null, 6 + message_id text not null, 7 + emote text not null, 8 + target_role_id text not null 9 + )
+8
server/src/server/reaction_role/sql/find.sql
··· 1 + -- finds an entry using guild_id, channel_id and message_id 2 + -- 3 + select 4 + * 5 + from 6 + reaction_roles 7 + where 8 + guild_id = $1 and channel_id = $2 and message_id = $3
+6
server/src/server/reaction_role/sql/insert.sql
··· 1 + -- insert a new reaction_role record into the database 2 + -- 3 + insert into reaction_roles 4 + (guild_id, channel_id, message_id, emote, target_role_id) 5 + values 6 + ($1, $2, $3, $4, $5)
+13
server/test/server_test.gleam
··· 1 + import gleeunit 2 + 3 + pub fn main() -> Nil { 4 + gleeunit.main() 5 + } 6 + 7 + // gleeunit test functions end in `_test` 8 + pub fn hello_world_test() { 9 + let name = "Joe" 10 + let greeting = "Hello, " <> name <> "!" 11 + 12 + assert greeting == "Hello, Joe!" 13 + }