fork of https://gitlab.com/grom-gleam/grom altered to work with https://fluxer.app
1
fork

Configure Feed

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

adjust to work with fluxer | add gateway.supervised()

nnuuvv 44a15ec1 3a3a4164

+112 -147
-3
mise.toml
··· 1 - [tools] 2 - erlang = "latest" 3 - gleam = "latest"
+1
src/grom.gleam
··· 12 12 13 13 pub type Error { 14 14 HttpError(httpc.HttpError) 15 + InsufficientPermissions 15 16 CouldNotDecode(json.DecodeError) 16 17 StatusCodeUnsuccessful(Response(String)) 17 18 ResponseNotValidUtf8(BitArray)
+1 -1
src/grom/emoji.gleam
··· 25 25 26 26 @internal 27 27 pub fn decoder() -> decode.Decoder(Emoji) { 28 - use id <- decode.field("id", decode.optional(decode.string)) 28 + use id <- decode.optional_field("id", None, decode.optional(decode.string)) 29 29 use name <- decode.field("name", decode.optional(decode.string)) 30 30 use role_ids <- decode.optional_field( 31 31 "roles",
+38 -72
src/grom/gateway.gleam
··· 38 38 import grom/internal/time_timestamp 39 39 import grom/invite 40 40 import grom/message 41 - import grom/message/reaction 42 41 import grom/modification.{type Modification, Skip} 43 42 import grom/soundboard 44 43 import grom/stage_instance.{type StageInstance} ··· 57 56 recommended_shards: Int, 58 57 session_start_limits: SessionStartLimits, 59 58 ) 60 - } 61 - 62 - pub type ReadyApplication { 63 - ReadyApplication(id: String, flags: List(application.Flag)) 64 59 } 65 60 66 61 pub type Shard { ··· 277 272 user: User, 278 273 guilds: List(guild.UnavailableGuild), 279 274 session_id: String, 280 - resume_gateway_url: String, 275 + resume_gateway_url: option.Option(String), 281 276 shard: Option(Shard), 282 - application: ReadyApplication, 283 277 ) 284 278 } 285 279 ··· 288 282 api_version: Int, 289 283 user: User, 290 284 guilds: List(guild.UnavailableGuild), 291 - application: ReadyApplication, 292 285 shard_count: Int, 293 286 ) 294 287 } ··· 579 572 member: Option(GuildMember), 580 573 emoji: Emoji, 581 574 message_author_id: Option(String), 582 - is_burst: Bool, 583 - burst_colors: Option(List(String)), 584 - type_: reaction.Type, 585 575 ) 586 576 } 587 577 ··· 592 582 message_id: String, 593 583 guild_id: Option(String), 594 584 emoji: Emoji, 595 - is_burst: Bool, 596 - type_: reaction.Type, 597 585 ) 598 586 } 599 587 ··· 805 793 heartbeat_manager: Subject(HeartbeatManagerMessage), 806 794 sequence: Option(Int), 807 795 session_id: String, 808 - resume_gateway_url: String, 796 + resume_gateway_url: option.Option(String), 809 797 ) 810 798 } 811 799 ··· 1258 1246 1259 1247 @internal 1260 1248 pub fn ready_message_decoder() -> decode.Decoder(ReadyMessage) { 1261 - use api_version <- decode.field("v", decode.int) 1249 + use api_version <- decode.optional_field( 1250 + "v", 1251 + None, 1252 + decode.optional(decode.int), 1253 + ) 1254 + let api_version = api_version |> option.unwrap(1) 1255 + 1262 1256 use user <- decode.field("user", user.decoder()) 1263 1257 use guilds <- decode.field( 1264 1258 "guilds", 1265 1259 decode.list(of: guild.unavailable_guild_decoder()), 1266 1260 ) 1267 1261 use session_id <- decode.field("session_id", decode.string) 1268 - use resume_gateway_url <- decode.field("resume_gateway_url", decode.string) 1262 + use resume_gateway_url <- decode.optional_field( 1263 + "resume_gateway_url", 1264 + None, 1265 + decode.optional(decode.string), 1266 + ) 1269 1267 use shard <- decode.optional_field( 1270 1268 "shard", 1271 1269 None, 1272 1270 decode.optional(shard_decoder()), 1273 1271 ) 1274 - use application <- decode.field("application", ready_application_decoder()) 1275 1272 1276 1273 decode.success(ReadyMessage( 1277 1274 api_version:, ··· 1280 1277 session_id:, 1281 1278 resume_gateway_url:, 1282 1279 shard:, 1283 - application:, 1284 1280 )) 1285 1281 } 1286 1282 ··· 1289 1285 use id <- decode.field(0, decode.int) 1290 1286 use num_shards <- decode.field(1, decode.int) 1291 1287 decode.success(Shard(id:, num_shards:)) 1292 - } 1293 - 1294 - @internal 1295 - pub fn ready_application_decoder() -> decode.Decoder(ReadyApplication) { 1296 - use id <- decode.field("id", decode.string) 1297 - use flags <- decode.field("flags", flags.decoder(application.bits_flags())) 1298 - decode.success(ReadyApplication(id:, flags:)) 1299 1288 } 1300 1289 1301 1290 @internal ··· 1777 1766 decode.success(Some(invite.ForEmbeddedApplication(application:))) 1778 1767 } 1779 1768 Some(_) -> 1780 - decode.failure( 1781 - Some( 1782 - invite.ForStream(User( 1783 - "", 1784 - "", 1785 - "", 1786 - None, 1787 - None, 1788 - None, 1789 - None, 1790 - None, 1791 - None, 1792 - None, 1793 - None, 1794 - None, 1795 - None, 1796 - None, 1797 - None, 1798 - None, 1799 - None, 1800 - )), 1801 - ), 1802 - "TargetType", 1803 - ) 1769 + decode.failure(Some(invite.ForStream(user.empty())), "TargetType") 1804 1770 None -> decode.success(None) 1805 1771 }) 1772 + 1806 1773 use is_temporary <- decode.field("temporary", decode.bool) 1807 1774 use uses <- decode.field("uses", decode.int) 1808 1775 use expires_at <- decode.field( ··· 1852 1819 None, 1853 1820 decode.optional(guild_member.decoder()), 1854 1821 ) 1855 - use mentions <- decode.field("mentions", decode.list(user.decoder())) 1822 + use mentions <- decode.optional_field( 1823 + "mentions", 1824 + None, 1825 + decode.optional(decode.list(user.decoder())), 1826 + ) 1827 + let mentions = mentions |> option.unwrap([]) 1856 1828 1857 1829 decode.success(MessageCreatedMessage(message:, guild_id:, member:, mentions:)) 1858 1830 } ··· 1930 1902 None, 1931 1903 decode.optional(decode.string), 1932 1904 ) 1933 - use is_burst <- decode.field("burst", decode.bool) 1934 - use burst_colors <- decode.optional_field( 1935 - "burst_colors", 1936 - None, 1937 - decode.optional(decode.list(decode.string)), 1938 - ) 1939 - use type_ <- decode.field("type", reaction.type_decoder()) 1940 1905 decode.success(MessageReactionCreatedMessage( 1941 1906 user_id:, 1942 1907 channel_id:, ··· 1945 1910 member:, 1946 1911 emoji:, 1947 1912 message_author_id:, 1948 - is_burst:, 1949 - burst_colors:, 1950 - type_:, 1951 1913 )) 1952 1914 } 1953 1915 ··· 1964 1926 decode.optional(decode.string), 1965 1927 ) 1966 1928 use emoji <- decode.field("emoji", emoji.decoder()) 1967 - use is_burst <- decode.field("burst", decode.bool) 1968 - use type_ <- decode.field("type", reaction.type_decoder()) 1969 1929 decode.success(MessageReactionDeletedMessage( 1970 1930 user_id:, 1971 1931 channel_id:, 1972 1932 message_id:, 1973 1933 guild_id:, 1974 1934 emoji:, 1975 - is_burst:, 1976 - type_:, 1977 1935 )) 1978 1936 } 1979 1937 ··· 2372 2330 /// You should hold the gateway subject in your state. 2373 2331 /// You'll send user messages to that subject. 2374 2332 pub fn new_with_initializer( 2375 - init: fn(Subject(Message)) -> Result(user_state, String), 2376 - identify: BaseIdentifyMessage, 2377 - data: Data, 2333 + init init: fn(Subject(Message)) -> Result(user_state, String), 2334 + identify identify: BaseIdentifyMessage, 2335 + data data: Data, 2378 2336 ) -> Builder(user_state) { 2379 2337 Builder( 2380 2338 identify:, ··· 2486 2444 |> actor.start 2487 2445 } 2488 2446 2447 + pub fn supervised( 2448 + builder: Builder(state), 2449 + ) -> supervision.ChildSpecification(Subject(Message)) { 2450 + supervision.supervisor(fn() { start(builder) }) 2451 + } 2452 + 2489 2453 /// Returns the ID of the shard that will handle an event from a specified guild. 2490 2454 /// Some things to mind: 2491 2455 /// * You can get the shard count from `gateway.Data` (for the recommended amount of shards), or it could be a number you specified when you created the gateway. ··· 2566 2530 api_version: ready.api_version, 2567 2531 user: ready.user, 2568 2532 guilds: ready.guilds, 2569 - application: ready.application, 2570 2533 shard_count: 1, 2571 2534 ) 2572 2535 } ··· 2683 2646 let request_url = 2684 2647 builder.data.url 2685 2648 |> string.replace(each: "wss://", with: "https://") 2686 - |> string.append(suffix: "?v=10&encoding=json") 2649 + |> string.append(suffix: "?v=1&encoding=json") 2687 2650 2688 2651 use request <- result.try( 2689 2652 request.to(request_url) ··· 2772 2735 ResumingInfo( 2773 2736 heartbeat_manager: connection_state.heartbeat_manager, 2774 2737 sequence: connection_state.sequence, 2775 - resume_gateway_url: connection_state.resume_gateway_url, 2738 + resume_gateway_url: connection_state.resume_gateway_url 2739 + |> option.unwrap(connection_state.gateway_url), 2776 2740 session_id: connection_state.session_id, 2777 2741 ), 2778 2742 ) ··· 2784 2748 let request_url = 2785 2749 resuming_info.resume_gateway_url 2786 2750 |> string.replace(each: "wss://", with: "https://") 2787 - |> string.append(suffix: "?v=10&encoding=json") 2751 + |> string.append(suffix: "?v=1&encoding=json") 2788 2752 2789 2753 let request_result = 2790 2754 request.to(request_url) ··· 3181 3145 ResumingInfo( 3182 3146 heartbeat_manager: connection_state.heartbeat_manager, 3183 3147 sequence: connection_state.sequence, 3184 - resume_gateway_url: connection_state.resume_gateway_url, 3148 + resume_gateway_url: connection_state.resume_gateway_url 3149 + |> option.unwrap(connection_state.gateway_url), 3185 3150 session_id: connection_state.session_id, 3186 3151 ), 3187 3152 ) ··· 3470 3435 3471 3436 fn parse_message(text_message: String) -> Result(ReceivedMessage, grom.Error) { 3472 3437 text_message 3438 + // |> echo as "message" 3473 3439 |> json.parse(using: message_decoder()) 3474 3440 |> result.map_error(grom.CouldNotDecode) 3475 3441 }
+6 -8
src/grom/guild/role.gleam
··· 8 8 import gleam/result 9 9 import grom 10 10 import grom/image 11 - import grom/internal/flags 12 11 import grom/internal/rest 13 12 import grom/modification.{type Modification, Skip} 14 13 import grom/permission.{type Permission} ··· 25 24 unicode_emoji: Option(String), 26 25 position: Int, 27 26 permissions: List(Permission), 28 - is_managed: Bool, 29 27 is_mentionable: Bool, 30 28 tags: Option(Tags), 31 - flags: List(Flag), 32 29 ) 33 30 } 34 31 ··· 96 93 pub fn decoder() -> decode.Decoder(Role) { 97 94 use id <- decode.field("id", decode.string) 98 95 use name <- decode.field("name", decode.string) 99 - use colors <- decode.field("colors", colors_decoder()) 96 + use colors <- decode.optional_field( 97 + "colors", 98 + Colors(0, None, None), 99 + colors_decoder(), 100 + ) 100 101 use is_hoisted <- decode.field("hoist", decode.bool) 101 102 use icon_hash <- decode.optional_field( 102 103 "icon", ··· 110 111 ) 111 112 use position <- decode.field("position", decode.int) 112 113 use permissions <- decode.field("permissions", permission.decoder()) 113 - use is_managed <- decode.field("managed", decode.bool) 114 114 use is_mentionable <- decode.field("mentionable", decode.bool) 115 115 use tags <- decode.optional_field( 116 116 "tags", 117 117 None, 118 118 decode.optional(tags_decoder()), 119 119 ) 120 - use flags <- decode.field("flags", flags.decoder(bits_flags())) 121 120 decode.success(Role( 122 121 id:, 123 122 name:, ··· 127 126 unicode_emoji:, 128 127 position:, 129 128 permissions:, 130 - is_managed:, 131 129 is_mentionable:, 132 130 tags:, 133 - flags:, 134 131 )) 135 132 } 136 133 ··· 271 268 272 269 // PUBLIC API FUNCTIONS -------------------------------------------------------- 273 270 271 + /// does not exist in fluxer apparently 274 272 pub fn get( 275 273 client: grom.Client, 276 274 for guild_id: String,
+12 -1
src/grom/guild_member.gleam
··· 122 122 None, 123 123 decode.optional(decode.bool), 124 124 ) 125 - use flags <- decode.field("flags", flags.decoder(bits_member_flags())) 125 + use flags <- decode.optional_field( 126 + "flags", 127 + None, 128 + decode.optional(flags.decoder(bits_member_flags())), 129 + ) 130 + let flags = flags |> option.unwrap([]) 131 + 126 132 use is_pending <- decode.optional_field( 127 133 "pending", 128 134 None, ··· 285 291 |> result.map_error(grom.CouldNotDecode) 286 292 } 287 293 294 + /// client: grom.Client, 295 + /// in guild_id: String, 296 + /// to user_id: String, 297 + /// id role_id: String, 298 + /// because reason: Option(String), 288 299 pub fn add_role( 289 300 client: grom.Client, 290 301 in guild_id: String,
+10 -7
src/grom/internal/rest.gleam
··· 16 16 17 17 // CONSTANTS ------------------------------------------------------------------- 18 18 19 - const discord_url: String = "discord.com" 19 + const discord_url: String = "api.fluxer.app" 20 20 21 - const discord_api_path: String = "api/v10" 21 + const discord_api_path: String = "/v1" 22 22 23 - const cdn_url: String = "cdn.discordapp.com" 23 + const cdn_url: String = "fluxerusercontent.com" 24 24 25 25 // INTERNAL FUNCTIONS ---------------------------------------------------------- 26 26 ··· 72 72 |> request.prepend_header("authorization", "Bot " <> client.token) 73 73 |> request.prepend_header( 74 74 "user-agent", 75 - "DiscordBot (https://github.com/folospior/grom, " <> grom.version() <> ")", 75 + "FluxerBot (https://github.com/folospior/grom, " <> grom.version() <> ")", 76 76 ) 77 77 |> request.prepend_header("content-type", "application/json") 78 78 } ··· 140 140 fn ensure_status_code_success( 141 141 response: Response(String), 142 142 ) -> Result(Response(String), grom.Error) { 143 - case status_code.is_successful(response.status) { 144 - True -> Ok(response) 145 - False -> Error(grom.StatusCodeUnsuccessful(response)) 143 + let is_success = status_code.is_successful(response.status) 144 + 145 + case response.status { 146 + 403 -> Error(grom.InsufficientPermissions) 147 + _ if is_success -> Ok(response) 148 + _ -> Error(grom.StatusCodeUnsuccessful(response)) 146 149 } 147 150 }
+3 -51
src/grom/invite.gleam
··· 20 20 import grom/internal/rest 21 21 import grom/internal/time_duration 22 22 import grom/internal/time_rfc3339 23 - import grom/user.{type User, User} 23 + import grom/user.{type User} 24 24 import multipart_form 25 25 import multipart_form/field 26 26 import splitter.{type Splitter} ··· 192 192 ) 193 193 decode.success(Some(ForEmbeddedApplication(application:))) 194 194 } 195 - Some(_) -> 196 - decode.failure( 197 - Some( 198 - ForStream(User( 199 - "", 200 - "", 201 - "", 202 - None, 203 - None, 204 - None, 205 - None, 206 - None, 207 - None, 208 - None, 209 - None, 210 - None, 211 - None, 212 - None, 213 - None, 214 - None, 215 - None, 216 - )), 217 - ), 218 - "TargetType", 219 - ) 195 + Some(_) -> decode.failure(Some(ForStream(user.empty())), "TargetType") 220 196 None -> decode.success(None) 221 197 }) 222 198 use approximate_presence_count <- decode.optional_field( ··· 293 269 ) 294 270 decode.success(Some(ForEmbeddedApplication(application:))) 295 271 } 296 - Some(_) -> 297 - decode.failure( 298 - Some( 299 - ForStream(User( 300 - "", 301 - "", 302 - "", 303 - None, 304 - None, 305 - None, 306 - None, 307 - None, 308 - None, 309 - None, 310 - None, 311 - None, 312 - None, 313 - None, 314 - None, 315 - None, 316 - None, 317 - )), 318 - ), 319 - "TargetType", 320 - ) 272 + Some(_) -> decode.failure(Some(ForStream(user.empty())), "TargetType") 321 273 None -> decode.success(None) 322 274 }) 323 275
+12 -4
src/grom/message.gleam
··· 454 454 "edited_timestamp", 455 455 decode.optional(time_rfc3339.decoder()), 456 456 ) 457 - use is_tts <- decode.field("tts", decode.bool) 457 + use is_tts <- decode.optional_field("tts", None, decode.optional(decode.bool)) 458 + let is_tts = option.unwrap(is_tts, False) 458 459 use mentions_everyone <- decode.field("mention_everyone", decode.bool) 459 - use mentions_users <- decode.field("mentions", decode.list(user.decoder())) 460 - use mentions_roles <- decode.field( 460 + use mentions_users <- decode.optional_field( 461 + "mentions", 462 + None, 463 + decode.optional(decode.list(user.decoder())), 464 + ) 465 + let mentions_users = mentions_users |> option.unwrap([]) 466 + use mentions_roles <- decode.optional_field( 461 467 "mention_roles", 462 - decode.list(decode.string), 468 + None, 469 + decode.optional(decode.list(decode.string)), 463 470 ) 471 + let mentions_roles = mentions_roles |> option.unwrap([]) 464 472 use mentions_channels <- decode.optional_field( 465 473 "mention_channels", 466 474 None,
+5
src/grom/message/reaction.gleam
··· 77 77 78 78 // PUBLIC API FUNCTIONS -------------------------------------------------------- 79 79 80 + /// client: grom.Client, 81 + /// in channel_id: String, 82 + /// on message_id: String, 83 + /// emoji emoji_id: String, 84 + /// 80 85 pub fn create( 81 86 client: grom.Client, 82 87 in channel_id: String,
+24
src/grom/user.gleam
··· 33 33 ) 34 34 } 35 35 36 + /// returns an empty user 37 + /// 38 + pub fn empty() { 39 + User( 40 + "", 41 + "", 42 + "", 43 + None, 44 + None, 45 + None, 46 + None, 47 + None, 48 + None, 49 + None, 50 + None, 51 + None, 52 + None, 53 + None, 54 + None, 55 + None, 56 + None, 57 + ) 58 + } 59 + 36 60 pub type AvatarDecorationData { 37 61 AvatarDecorationData(asset: String, sku_id: String) 38 62 }