diff options
| author | Adelyn Breelove <[email protected]> | 2018-11-29 12:55:10 -0700 |
|---|---|---|
| committer | Adelyn Breelove <[email protected]> | 2018-11-29 12:55:10 -0700 |
| commit | 473072f66e6c7e228b4f26730cbc7304941fb12b (patch) | |
| tree | cd689e7f53bafbf357c8be88e317251b2a4b7539 /lib | |
| parent | Clean up a bit (diff) | |
| download | disml-473072f66e6c7e228b4f26730cbc7304941fb12b.tar.xz disml-473072f66e6c7e228b4f26730cbc7304941fb12b.zip | |
functors!
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/client.ml | 61 | ||||
| -rw-r--r-- | lib/dis.ml | 0 | ||||
| -rw-r--r-- | lib/http.ml | 439 | ||||
| -rw-r--r-- | lib/s.ml | 31 | ||||
| -rw-r--r-- | lib/sharder.ml | 549 |
5 files changed, 562 insertions, 518 deletions
diff --git a/lib/client.ml b/lib/client.ml index e88b1d5..7adaae3 100644 --- a/lib/client.ml +++ b/lib/client.ml @@ -1,35 +1,42 @@ open Async -type t = { - sharder: Sharder.t Ivar.t; - handler: string Pipe.Writer.t; - token: string; -} +module Make(T : S.Token) = struct + include T + + module Http = Http.Make(T) + module Sharder = Sharder.Make(Http) -let create ~handler token = - { - sharder = Ivar.create (); - handler; - token; + type t = { + sharder: Sharder.t Ivar.t; + handler: string Pipe.Writer.t; + token: string; } -let start ?count client = - Sharder.start ?count client.token - >>| fun sharder -> - Ivar.fill_if_empty client.sharder sharder; - client + let init ~handler () = + { + sharder = Ivar.create (); + handler; + token; + } -let set_status ~status client = - Ivar.read client.sharder - >>= fun sharder -> - Sharder.set_status sharder status + let start ?count client = + Sharder.start ?count client.token + >>| fun sharder -> + Ivar.fill_if_empty client.sharder sharder; + client -let set_status_with ~f client = - Ivar.read client.sharder - >>= fun sharder -> - Sharder.set_status_with sharder f + let set_status ~status client = + Ivar.read client.sharder + >>= fun sharder -> + Sharder.set_status sharder status -let request_guild_members ~guild ?query ?limit client = - Ivar.read client.sharder - >>= fun sharder -> - Sharder.request_guild_members ~guild ?query ?limit sharder + let set_status_with ~f client = + Ivar.read client.sharder + >>= fun sharder -> + Sharder.set_status_with sharder f + + let request_guild_members ~guild ?query ?limit client = + Ivar.read client.sharder + >>= fun sharder -> + Sharder.request_guild_members ~guild ?query ?limit sharder +end
\ No newline at end of file diff --git a/lib/dis.ml b/lib/dis.ml deleted file mode 100644 index e69de29..0000000 --- a/lib/dis.ml +++ /dev/null diff --git a/lib/http.ml b/lib/http.ml index 8d0b679..a8e6b22 100644 --- a/lib/http.ml +++ b/lib/http.ml @@ -1,319 +1,322 @@ open Async open Cohttp -module Base = struct - exception Invalid_Method +module Make(T : S.Token) = struct + include T - let base_url = "https://discordapp.com/api/v7" - let cdn_url = "https://cdn.discordapp.com" + module Base = struct + exception Invalid_Method - let process_url path = - Uri.of_string (base_url ^ path) + let base_url = "https://discordapp.com/api/v7" + let cdn_url = "https://cdn.discordapp.com" - let process_request_body body = - body - |> Yojson.Basic.to_string - |> Cohttp_async.Body.of_string + let process_url path = + Uri.of_string (base_url ^ path) - let process_request_headers () = - let token = match Sys.getenv "DISCORD_TOKEN" with - | Some t -> t - | None -> failwith "Please provide a token" - in - let h = Header.init_with "User-Agent" "Dis.ml v0.1.0" in - let h = Header.add h "Authorization" ("Bot " ^ token) in - Header.add h "Content-Type" "application/json" + let process_request_body body = + body + |> Yojson.Basic.to_string + |> Cohttp_async.Body.of_string - (* TODO Finish processor *) - let process_response (_resp, body) = - body |> Cohttp_async.Body.to_string >>| Yojson.Basic.from_string + let process_request_headers () = + let h = Header.init () in + Header.add_list h [ + "User-Agent", "Dis.ml v0.1.0"; + "Authorization", ("Bot " ^ token); + "Content-Type", "application/json"; + ] - let request ?(body=`Null) m path = - let uri = process_url path in - let headers = process_request_headers () in - let body = process_request_body body in - (match m with - | `DELETE -> Cohttp_async.Client.delete ~headers ~body uri - | `GET -> Cohttp_async.Client.get ~headers uri - | `PATCH -> Cohttp_async.Client.patch ~headers ~body uri - | `POST -> Cohttp_async.Client.post ~headers ~body uri - | `PUT -> Cohttp_async.Client.put ~headers ~body uri - | _ -> raise Invalid_Method) - >>= process_response -end + (* TODO Finish processor *) + let process_response (_resp, body) = + body |> Cohttp_async.Body.to_string >>| Yojson.Basic.from_string -let get_gateway () = - Base.request `GET Endpoints.gateway + let request ?(body=`Null) m path = + let uri = process_url path in + let headers = process_request_headers () in + let body = process_request_body body in + (match m with + | `DELETE -> Cohttp_async.Client.delete ~headers ~body uri + | `GET -> Cohttp_async.Client.get ~headers uri + | `PATCH -> Cohttp_async.Client.patch ~headers ~body uri + | `POST -> Cohttp_async.Client.post ~headers ~body uri + | `PUT -> Cohttp_async.Client.put ~headers ~body uri + | _ -> raise Invalid_Method) + >>= process_response + end -let get_gateway_bot () = - Base.request `GET Endpoints.gateway_bot + let get_gateway () = + Base.request `GET Endpoints.gateway -let get_channel channel_id = - Base.request `GET (Endpoints.channel channel_id) + let get_gateway_bot () = + Base.request `GET Endpoints.gateway_bot -let modify_channel channel_id body = - Base.request ~body `PATCH (Endpoints.channel channel_id) + let get_channel channel_id = + Base.request `GET (Endpoints.channel channel_id) -let delete_channel channel_id = - Base.request `DELETE (Endpoints.channel channel_id) + let modify_channel channel_id body = + Base.request ~body `PATCH (Endpoints.channel channel_id) -let get_messages channel_id = - Base.request `GET (Endpoints.channel_messages channel_id) + let delete_channel channel_id = + Base.request `DELETE (Endpoints.channel channel_id) -let get_message channel_id message_id = - Base.request `GET (Endpoints.channel_message channel_id message_id) + let get_messages channel_id = + Base.request `GET (Endpoints.channel_messages channel_id) -let create_message channel_id body = - Base.request ~body:body `POST (Endpoints.channel_messages channel_id) + let get_message channel_id message_id = + Base.request `GET (Endpoints.channel_message channel_id message_id) -let create_reaction channel_id message_id emoji = - Base.request `PUT (Endpoints.channel_reaction_me channel_id message_id emoji) + let create_message channel_id body = + Base.request ~body:body `POST (Endpoints.channel_messages channel_id) -let delete_own_reaction channel_id message_id emoji = - Base.request `DELETE (Endpoints.channel_reaction_me channel_id message_id emoji) + let create_reaction channel_id message_id emoji = + Base.request `PUT (Endpoints.channel_reaction_me channel_id message_id emoji) -let delete_reaction channel_id message_id emoji user_id = - Base.request `DELETE (Endpoints.channel_reaction channel_id message_id emoji user_id) + let delete_own_reaction channel_id message_id emoji = + Base.request `DELETE (Endpoints.channel_reaction_me channel_id message_id emoji) -let get_reactions channel_id message_id emoji = - Base.request `GET (Endpoints.channel_reactions_get channel_id message_id emoji) + let delete_reaction channel_id message_id emoji user_id = + Base.request `DELETE (Endpoints.channel_reaction channel_id message_id emoji user_id) -let delete_reactions channel_id message_id = - Base.request `DELETE (Endpoints.channel_reactions_delete channel_id message_id) + let get_reactions channel_id message_id emoji = + Base.request `GET (Endpoints.channel_reactions_get channel_id message_id emoji) -let edit_message channel_id message_id body = - Base.request ~body `PATCH (Endpoints.channel_message channel_id message_id) + let delete_reactions channel_id message_id = + Base.request `DELETE (Endpoints.channel_reactions_delete channel_id message_id) -let delete_message channel_id message_id = - Base.request `DELETE (Endpoints.channel_message channel_id message_id) + let edit_message channel_id message_id body = + Base.request ~body `PATCH (Endpoints.channel_message channel_id message_id) -let bulk_delete channel_id body = - Base.request ~body `POST (Endpoints.channel_bulk_delete channel_id) + let delete_message channel_id message_id = + Base.request `DELETE (Endpoints.channel_message channel_id message_id) -let edit_channel_permissions channel_id overwrite_id body = - Base.request ~body `PUT (Endpoints.channel_permission channel_id overwrite_id) + let bulk_delete channel_id body = + Base.request ~body `POST (Endpoints.channel_bulk_delete channel_id) -let get_channel_invites channel_id = - Base.request `GET (Endpoints.channel_invites channel_id) + let edit_channel_permissions channel_id overwrite_id body = + Base.request ~body `PUT (Endpoints.channel_permission channel_id overwrite_id) -let create_channel_invite channel_id body = - Base.request ~body `POST (Endpoints.channel_invites channel_id) + let get_channel_invites channel_id = + Base.request `GET (Endpoints.channel_invites channel_id) -let delete_channel_permission channel_id overwrite_id = - Base.request `DELETE (Endpoints.channel_permission channel_id overwrite_id) + let create_channel_invite channel_id body = + Base.request ~body `POST (Endpoints.channel_invites channel_id) -let broadcast_typing channel_id = - Base.request `POST (Endpoints.channel_typing channel_id) + let delete_channel_permission channel_id overwrite_id = + Base.request `DELETE (Endpoints.channel_permission channel_id overwrite_id) -let get_pinned_messages channel_id = - Base.request `GET (Endpoints.channel_pins channel_id) + let broadcast_typing channel_id = + Base.request `POST (Endpoints.channel_typing channel_id) -let pin_message channel_id message_id = - Base.request `PUT (Endpoints.channel_pin channel_id message_id) + let get_pinned_messages channel_id = + Base.request `GET (Endpoints.channel_pins channel_id) -let unpin_message channel_id message_id = - Base.request `DELETE (Endpoints.channel_pin channel_id message_id) + let pin_message channel_id message_id = + Base.request `PUT (Endpoints.channel_pin channel_id message_id) -let group_recipient_add channel_id user_id = - Base.request `PUT (Endpoints.group_recipient channel_id user_id) + let unpin_message channel_id message_id = + Base.request `DELETE (Endpoints.channel_pin channel_id message_id) -let group_recipient_remove channel_id user_id = - Base.request `DELETE (Endpoints.group_recipient channel_id user_id) + let group_recipient_add channel_id user_id = + Base.request `PUT (Endpoints.group_recipient channel_id user_id) -let get_emojis guild_id = - Base.request `GET (Endpoints.guild_emojis guild_id) + let group_recipient_remove channel_id user_id = + Base.request `DELETE (Endpoints.group_recipient channel_id user_id) -let get_emoji guild_id emoji_id = - Base.request `GET (Endpoints.guild_emoji guild_id emoji_id) + let get_emojis guild_id = + Base.request `GET (Endpoints.guild_emojis guild_id) -let create_emoji guild_id body = - Base.request ~body `POST (Endpoints.guild_emojis guild_id) + let get_emoji guild_id emoji_id = + Base.request `GET (Endpoints.guild_emoji guild_id emoji_id) -let edit_emoji guild_id emoji_id body = - Base.request ~body `PATCH (Endpoints.guild_emoji guild_id emoji_id) + let create_emoji guild_id body = + Base.request ~body `POST (Endpoints.guild_emojis guild_id) -let delete_emoji guild_id emoji_id = - Base.request `DELETE (Endpoints.guild_emoji guild_id emoji_id) + let edit_emoji guild_id emoji_id body = + Base.request ~body `PATCH (Endpoints.guild_emoji guild_id emoji_id) -let create_guild body = - Base.request ~body `POST Endpoints.guilds + let delete_emoji guild_id emoji_id = + Base.request `DELETE (Endpoints.guild_emoji guild_id emoji_id) -let get_guild guild_id = - Base.request `GET (Endpoints.guild guild_id) + let create_guild body = + Base.request ~body `POST Endpoints.guilds -let edit_guild guild_id body = - Base.request ~body `PATCH (Endpoints.guild guild_id) + let get_guild guild_id = + Base.request `GET (Endpoints.guild guild_id) -let delete_guild guild_id = - Base.request `DELETE (Endpoints.guild guild_id) + let edit_guild guild_id body = + Base.request ~body `PATCH (Endpoints.guild guild_id) -let get_guild_channels guild_id = - Base.request `GET (Endpoints.guild_channels guild_id) + let delete_guild guild_id = + Base.request `DELETE (Endpoints.guild guild_id) -let create_guild_channel guild_id body = - Base.request ~body `POST (Endpoints.guild_channels guild_id) + let get_guild_channels guild_id = + Base.request `GET (Endpoints.guild_channels guild_id) -let modify_guild_channel_positions guild_id body = - Base.request ~body `PATCH (Endpoints.guild_channels guild_id) + let create_guild_channel guild_id body = + Base.request ~body `POST (Endpoints.guild_channels guild_id) -let get_member guild_id user_id = - Base.request `GET (Endpoints.guild_member guild_id user_id) + let modify_guild_channel_positions guild_id body = + Base.request ~body `PATCH (Endpoints.guild_channels guild_id) -let get_members guild_id = - Base.request `GET (Endpoints.guild_members guild_id) + let get_member guild_id user_id = + Base.request `GET (Endpoints.guild_member guild_id user_id) -let add_member guild_id user_id body = - Base.request ~body `PUT (Endpoints.guild_member guild_id user_id) + let get_members guild_id = + Base.request `GET (Endpoints.guild_members guild_id) -let edit_member guild_id user_id body = - Base.request ~body `PATCH (Endpoints.guild_member guild_id user_id) + let add_member guild_id user_id body = + Base.request ~body `PUT (Endpoints.guild_member guild_id user_id) -let remove_member guild_id user_id = - Base.request `DELETE (Endpoints.guild_member guild_id user_id) + let edit_member guild_id user_id body = + Base.request ~body `PATCH (Endpoints.guild_member guild_id user_id) -let change_nickname guild_id body = - Base.request ~body `PATCH (Endpoints.guild_me_nick guild_id) + let remove_member guild_id user_id = + Base.request `DELETE (Endpoints.guild_member guild_id user_id) -let add_member_role guild_id user_id role_id = - Base.request `PUT (Endpoints.guild_member_role guild_id user_id role_id) + let change_nickname guild_id body = + Base.request ~body `PATCH (Endpoints.guild_me_nick guild_id) -let remove_member_role guild_id user_id role_id = - Base.request `DELETE (Endpoints.guild_member_role guild_id user_id role_id) + let add_member_role guild_id user_id role_id = + Base.request `PUT (Endpoints.guild_member_role guild_id user_id role_id) -let get_bans guild_id = - Base.request `GET (Endpoints.guild_bans guild_id) + let remove_member_role guild_id user_id role_id = + Base.request `DELETE (Endpoints.guild_member_role guild_id user_id role_id) -let get_ban guild_id user_id = - Base.request `GET (Endpoints.guild_ban guild_id user_id) + let get_bans guild_id = + Base.request `GET (Endpoints.guild_bans guild_id) -let guild_ban_add guild_id user_id body = - Base.request ~body `PUT (Endpoints.guild_ban guild_id user_id) + let get_ban guild_id user_id = + Base.request `GET (Endpoints.guild_ban guild_id user_id) -let guild_ban_remove guild_id user_id = - Base.request `DELETE (Endpoints.guild_ban guild_id user_id) + let guild_ban_add guild_id user_id body = + Base.request ~body `PUT (Endpoints.guild_ban guild_id user_id) -let get_roles guild_id = - Base.request `GET (Endpoints.guild_roles guild_id) + let guild_ban_remove guild_id user_id = + Base.request `DELETE (Endpoints.guild_ban guild_id user_id) -let guild_role_add guild_id body = - Base.request ~body `POST (Endpoints.guild_roles guild_id) + let get_roles guild_id = + Base.request `GET (Endpoints.guild_roles guild_id) -let guild_roles_edit guild_id body = - Base.request ~body `PATCH (Endpoints.guild_roles guild_id) + let guild_role_add guild_id body = + Base.request ~body `POST (Endpoints.guild_roles guild_id) -let guild_role_edit guild_id role_id body = - Base.request ~body `PATCH (Endpoints.guild_role guild_id role_id) + let guild_roles_edit guild_id body = + Base.request ~body `PATCH (Endpoints.guild_roles guild_id) -let guild_role_remove guild_id role_id = - Base.request `DELETE (Endpoints.guild_role guild_id role_id) + let guild_role_edit guild_id role_id body = + Base.request ~body `PATCH (Endpoints.guild_role guild_id role_id) -let guild_prune_count guild_id = - Base.request `GET (Endpoints.guild_prune guild_id) + let guild_role_remove guild_id role_id = + Base.request `DELETE (Endpoints.guild_role guild_id role_id) -let guild_prune_start guild_id body = - Base.request ~body `POST (Endpoints.guild_prune guild_id) + let guild_prune_count guild_id = + Base.request `GET (Endpoints.guild_prune guild_id) -let get_guild_voice_regions guild_id = - Base.request `GET (Endpoints.guild_voice_regions guild_id) + let guild_prune_start guild_id body = + Base.request ~body `POST (Endpoints.guild_prune guild_id) -let get_guild_invites guild_id = - Base.request `GET (Endpoints.guild_invites guild_id) + let get_guild_voice_regions guild_id = + Base.request `GET (Endpoints.guild_voice_regions guild_id) -let get_integrations guild_id = - Base.request `GET (Endpoints.guild_integrations guild_id) + let get_guild_invites guild_id = + Base.request `GET (Endpoints.guild_invites guild_id) -let add_integration guild_id body = - Base.request ~body `POST (Endpoints.guild_integrations guild_id) + let get_integrations guild_id = + Base.request `GET (Endpoints.guild_integrations guild_id) -let edit_integration guild_id integration_id body = - Base.request ~body `POST (Endpoints.guild_integration guild_id integration_id) + let add_integration guild_id body = + Base.request ~body `POST (Endpoints.guild_integrations guild_id) -let delete_integration guild_id integration_id = - Base.request `DELETE (Endpoints.guild_integration guild_id integration_id) + let edit_integration guild_id integration_id body = + Base.request ~body `POST (Endpoints.guild_integration guild_id integration_id) -let sync_integration guild_id integration_id = - Base.request `POST (Endpoints.guild_integration_sync guild_id integration_id) + let delete_integration guild_id integration_id = + Base.request `DELETE (Endpoints.guild_integration guild_id integration_id) -let get_guild_embed guild_id = - Base.request `GET (Endpoints.guild_embed guild_id) + let sync_integration guild_id integration_id = + Base.request `POST (Endpoints.guild_integration_sync guild_id integration_id) -let edit_guild_embed guild_id body = - Base.request ~body `PATCH (Endpoints.guild_embed guild_id) + let get_guild_embed guild_id = + Base.request `GET (Endpoints.guild_embed guild_id) -let get_vanity_url guild_id = - Base.request `GET (Endpoints.guild_vanity_url guild_id) + let edit_guild_embed guild_id body = + Base.request ~body `PATCH (Endpoints.guild_embed guild_id) -let get_invite invite_code = - Base.request `GET (Endpoints.invite invite_code) + let get_vanity_url guild_id = + Base.request `GET (Endpoints.guild_vanity_url guild_id) -let delete_invite invite_code = - Base.request `DELETE (Endpoints.invite invite_code) + let get_invite invite_code = + Base.request `GET (Endpoints.invite invite_code) -let get_current_user () = - Base.request `GET Endpoints.me + let delete_invite invite_code = + Base.request `DELETE (Endpoints.invite invite_code) -let edit_current_user body = - Base.request ~body `PATCH Endpoints.me + let get_current_user () = + Base.request `GET Endpoints.me -let get_guilds () = - Base.request `GET Endpoints.me_guilds + let edit_current_user body = + Base.request ~body `PATCH Endpoints.me -let leave_guild guild_id = - Base.request `DELETE (Endpoints.me_guild guild_id) + let get_guilds () = + Base.request `GET Endpoints.me_guilds -let get_private_channels () = - Base.request `GET Endpoints.me_channels + let leave_guild guild_id = + Base.request `DELETE (Endpoints.me_guild guild_id) -let create_dm body = - Base.request ~body `POST Endpoints.me_channels + let get_private_channels () = + Base.request `GET Endpoints.me_channels -let create_group_dm body = - Base.request ~body `POST Endpoints.me_channels + let create_dm body = + Base.request ~body `POST Endpoints.me_channels -let get_connections () = - Base.request `GET Endpoints.me_connections + let create_group_dm body = + Base.request ~body `POST Endpoints.me_channels -let get_user user_id = - Base.request `GET (Endpoints.user user_id) + let get_connections () = + Base.request `GET Endpoints.me_connections -let get_voice_regions () = - Base.request `GET Endpoints.regions + let get_user user_id = + Base.request `GET (Endpoints.user user_id) -let create_webhook channel_id body = - Base.request ~body `POST (Endpoints.webhooks_channel channel_id) + let get_voice_regions () = + Base.request `GET Endpoints.regions -let get_channel_webhooks channel_id = - Base.request `GET (Endpoints.webhooks_channel channel_id) + let create_webhook channel_id body = + Base.request ~body `POST (Endpoints.webhooks_channel channel_id) -let get_guild_webhooks guild_id = - Base.request `GET (Endpoints.webhooks_guild guild_id) + let get_channel_webhooks channel_id = + Base.request `GET (Endpoints.webhooks_channel channel_id) -let get_webhook webhook_id = - Base.request `GET (Endpoints.webhook webhook_id) + let get_guild_webhooks guild_id = + Base.request `GET (Endpoints.webhooks_guild guild_id) -let get_webhook_with_token webhook_id token = - Base.request `GET (Endpoints.webhook_token webhook_id token) + let get_webhook webhook_id = + Base.request `GET (Endpoints.webhook webhook_id) -let edit_webhook webhook_id body = - Base.request ~body `PATCH (Endpoints.webhook webhook_id) + let get_webhook_with_token webhook_id token = + Base.request `GET (Endpoints.webhook_token webhook_id token) -let edit_webhook_with_token webhook_id token body = - Base.request ~body `PATCH (Endpoints.webhook_token webhook_id token) + let edit_webhook webhook_id body = + Base.request ~body `PATCH (Endpoints.webhook webhook_id) -let delete_webhook webhook_id = - Base.request `DELETE (Endpoints.webhook webhook_id) + let edit_webhook_with_token webhook_id token body = + Base.request ~body `PATCH (Endpoints.webhook_token webhook_id token) -let delete_webhook_with_token webhook_id token = - Base.request `DELETE (Endpoints.webhook_token webhook_id token) + let delete_webhook webhook_id = + Base.request `DELETE (Endpoints.webhook webhook_id) -let execute_webhook webhook_id token body = - Base.request ~body `POST (Endpoints.webhook_token webhook_id token) + let delete_webhook_with_token webhook_id token = + Base.request `DELETE (Endpoints.webhook_token webhook_id token) -let execute_slack_webhook webhook_id token body = - Base.request ~body `POST (Endpoints.webhook_slack webhook_id token) + let execute_webhook webhook_id token body = + Base.request ~body `POST (Endpoints.webhook_token webhook_id token) -let execute_git_webhook webhook_id token body = - Base.request ~body `POST (Endpoints.webhook_git webhook_id token) + let execute_slack_webhook webhook_id token body = + Base.request ~body `POST (Endpoints.webhook_slack webhook_id token) -let get_audit_logs guild_id body = - Base.request ~body `GET (Endpoints.guild_audit_logs guild_id)
\ No newline at end of file + let execute_git_webhook webhook_id token body = + Base.request ~body `POST (Endpoints.webhook_git webhook_id token) + + let get_audit_logs guild_id body = + Base.request ~body `GET (Endpoints.guild_audit_logs guild_id) +end
\ No newline at end of file diff --git a/lib/s.ml b/lib/s.ml new file mode 100644 index 0000000..92259a0 --- /dev/null +++ b/lib/s.ml @@ -0,0 +1,31 @@ +open Async +open Cohttp + +module type Token = sig + val token : string +end + +module type Http = sig + module Base : sig + exception Invalid_Method + + val base_url : string + + val process_url : string -> Uri.t + val process_request_body : Yojson.Basic.json -> Cohttp_async.Body.t + val process_request_headers : unit -> Headers.t + + val process_response : + Cohttp_async.Response.t * Cohttp_async.Body.t -> + Yojson.Basic.json + + val request : + ?body:Yojson.Basic.json -> + [ `Delete | `Get | `Patch | `Post | `Put ] -> + string -> + Yojson.Basic.json Deferred.t + end + + (* TODO add abstraction sigs *) + val token : string +end
\ No newline at end of file diff --git a/lib/sharder.ml b/lib/sharder.ml index 7f66b4e..1fa97a0 100644 --- a/lib/sharder.ml +++ b/lib/sharder.ml @@ -2,300 +2,303 @@ open Async open Core open Websocket_async -exception Invalid_Payload -exception Failure_to_Establish_Heartbeat +module Make(H: S.Http) = struct + exception Invalid_Payload + exception Failure_to_Establish_Heartbeat -module Shard = struct - type shard = { - hb: unit Ivar.t option; - seq: int; - session: string option; - pipe: Frame.t Pipe.Reader.t * Frame.t Pipe.Writer.t; - ready: unit Ivar.t; - token: string; - url: string; - id: int * int; - } + let token = H.token - type 'a t = { - mutable shard: 'a; - mutable binds: ('a -> unit) list; - } + module Shard = struct + type shard = { + hb: unit Ivar.t option; + seq: int; + session: string option; + pipe: Frame.t Pipe.Reader.t * Frame.t Pipe.Writer.t; + ready: unit Ivar.t; + url: string; + id: int * int; + } - let identify_lock = Mutex.create () + type 'a t = { + mutable shard: 'a; + mutable binds: ('a -> unit) list; + } - let bind ~f t = - t.binds <- f :: t.binds + let identify_lock = Mutex.create () - let parse (frame:[`Ok of Frame.t | `Eof]) = - match frame with - | `Ok s -> begin - let open Frame.Opcode in - match s.opcode with - | Text -> Some (Yojson.Basic.from_string s.content) - | _ -> None - end - | `Eof -> None + let bind ~f t = + t.binds <- f :: t.binds - let push_frame ?payload ~ev shard = - print_endline @@ "Pushing frame. OP: " ^ Opcode.to_string @@ ev; - let content = match payload with - | None -> "" - | Some p -> - Yojson.Basic.to_string @@ `Assoc [ - ("op", `Int (Opcode.to_int ev)); - ("d", p); - ] - in - let (_, write) = shard.pipe in - Pipe.write_if_open write @@ Frame.create ~content () - >>| fun () -> - shard + let parse (frame:[`Ok of Frame.t | `Eof]) = + match frame with + | `Ok s -> begin + let open Frame.Opcode in + match s.opcode with + | Text -> Some (Yojson.Basic.from_string s.content) + | _ -> None + end + | `Eof -> None - let heartbeat shard = - let payload = match shard.seq with - | 0 -> `Null - | i -> `Int i - in - push_frame ~payload ~ev:HEARTBEAT shard + let push_frame ?payload ~ev shard = + print_endline @@ "Pushing frame. OP: " ^ Opcode.to_string @@ ev; + let content = match payload with + | None -> "" + | Some p -> + Yojson.Basic.to_string @@ `Assoc [ + ("op", `Int (Opcode.to_int ev)); + ("d", p); + ] + in + let (_, write) = shard.pipe in + Pipe.write_if_open write @@ Frame.create ~content () + >>| fun () -> + shard - let dispatch ~payload shard = - let module J = Yojson.Basic.Util in - let seq = J.(member "s" payload |> to_int) in - let t = J.(member "t" payload |> to_string) in - let data = J.member "d" payload in - let session = J.(member "session_id" data |> to_string_option) in - if t = "READY" then begin - Ivar.fill_if_empty shard.ready (); - end; - return { shard with - seq = seq; - session = session; - } + let heartbeat shard = + let payload = match shard.seq with + | 0 -> `Null + | i -> `Int i + in + push_frame ~payload ~ev:HEARTBEAT shard - let set_status ~status shard = - let payload = match status with - | `Assoc [("name", `String name); ("type", `Int t)] -> - `Assoc [ - ("status", `String "online"); - ("afk", `Bool false); - ("since", `Null); - ("game", `Assoc [ - ("name", `String name); - ("type", `Int t) - ]) - ] - | `String name -> - `Assoc [ - ("status", `String "online"); - ("afk", `Bool false); - ("since", `Null); - ("game", `Assoc [ - ("name", `String name); - ("type", `Int 0) - ]) - ] - | _ -> raise Invalid_Payload - in - Ivar.read shard.ready >>= fun _ -> - push_frame ~payload ~ev:STATUS_UPDATE shard + let dispatch ~payload shard = + let module J = Yojson.Basic.Util in + let seq = J.(member "s" payload |> to_int) in + let t = J.(member "t" payload |> to_string) in + let data = J.member "d" payload in + let session = J.(member "session_id" data |> to_string_option) in + if t = "READY" then begin + Ivar.fill_if_empty shard.ready (); + end; + return { shard with + seq = seq; + session = session; + } - let request_guild_members ~guild ?(query="") ?(limit=0) shard = - let payload = `Assoc [ - ("guild_id", `String (string_of_int guild)); - ("query", `String query); - ("limit", `Int limit); - ] in - Ivar.read shard.ready >>= fun _ -> - push_frame ~payload ~ev:REQUEST_GUILD_MEMBERS shard + let set_status ~status shard = + let payload = match status with + | `Assoc [("name", `String name); ("type", `Int t)] -> + `Assoc [ + ("status", `String "online"); + ("afk", `Bool false); + ("since", `Null); + ("game", `Assoc [ + ("name", `String name); + ("type", `Int t) + ]) + ] + | `String name -> + `Assoc [ + ("status", `String "online"); + ("afk", `Bool false); + ("since", `Null); + ("game", `Assoc [ + ("name", `String name); + ("type", `Int 0) + ]) + ] + | _ -> raise Invalid_Payload + in + Ivar.read shard.ready >>= fun _ -> + push_frame ~payload ~ev:STATUS_UPDATE shard - let initialize ?data shard = - let module J = Yojson.Basic.Util in - let hb = match shard.hb with - | None -> begin - match data with - | Some data -> - let hb_interval = J.(member "heartbeat_interval" data |> to_int) in - let finished = Ivar.create () in - Clock.every' - ~continue_on_error:true - ~finished - (Core.Time.Span.create ~ms:hb_interval ()) - (fun () -> heartbeat shard >>= fun _ -> return ()); - finished - | None -> raise Failure_to_Establish_Heartbeat - end - | Some s -> s - in - let shard = { shard with hb = Some hb; } in - let (cur, max) = shard.id in - let shards = [`Int cur; `Int max] in - match shard.session with - | None -> begin - Mutex.lock identify_lock; + let request_guild_members ~guild ?(query="") ?(limit=0) shard = let payload = `Assoc [ - ("token", `String shard.token); - ("properties", `Assoc [ - ("$os", `String Sys.os_type); - ("$device", `String "dis.ml"); - ("$browser", `String "dis.ml") - ]); - ("compress", `Bool false); (* TODO add compression handling*) - ("large_threshold", `Int 250); - ("shard", `List shards); + ("guild_id", `String (string_of_int guild)); + ("query", `String query); + ("limit", `Int limit); ] in - push_frame ~payload ~ev:IDENTIFY shard - >>| fun s -> begin - Clock.after (Core.Time.Span.create ~sec:5 ()) - >>> (fun _ -> Mutex.unlock identify_lock); - s + Ivar.read shard.ready >>= fun _ -> + push_frame ~payload ~ev:REQUEST_GUILD_MEMBERS shard + + let initialize ?data shard = + let module J = Yojson.Basic.Util in + let hb = match shard.hb with + | None -> begin + match data with + | Some data -> + let hb_interval = J.(member "heartbeat_interval" data |> to_int) in + let finished = Ivar.create () in + Clock.every' + ~continue_on_error:true + ~finished + (Core.Time.Span.create ~ms:hb_interval ()) + (fun () -> heartbeat shard >>= fun _ -> return ()); + finished + | None -> raise Failure_to_Establish_Heartbeat end - end - | Some s -> - let payload = `Assoc [ - ("token", `String shard.token); - ("session_id", `String s); - ("seq", `Int shard.seq) - ] in - push_frame ~payload ~ev:RESUME shard + | Some s -> s + in + let shard = { shard with hb = Some hb; } in + let (cur, max) = shard.id in + let shards = [`Int cur; `Int max] in + match shard.session with + | None -> begin + Mutex.lock identify_lock; + let payload = `Assoc [ + ("token", `String shard.token); + ("properties", `Assoc [ + ("$os", `String Sys.os_type); + ("$device", `String "dis.ml"); + ("$browser", `String "dis.ml") + ]); + ("compress", `Bool false); (* TODO add compression handling*) + ("large_threshold", `Int 250); + ("shard", `List shards); + ] in + push_frame ~payload ~ev:IDENTIFY shard + >>| fun s -> begin + Clock.after (Core.Time.Span.create ~sec:5 ()) + >>> (fun _ -> Mutex.unlock identify_lock); + s + end + end + | Some s -> + let payload = `Assoc [ + ("token", `String shard.token); + ("session_id", `String s); + ("seq", `Int shard.seq) + ] in + push_frame ~payload ~ev:RESUME shard - let handle_frame ~f shard = - let module J = Yojson.Basic.Util in - let op = J.(member "op" f |> to_int) - |> Opcode.from_int - in - match op with - | DISPATCH -> dispatch ~payload:f shard - | HEARTBEAT -> heartbeat shard - | INVALID_SESSION -> begin - if J.(member "d" f |> to_bool) then - initialize shard - else begin - initialize { shard with session = None; } + let handle_frame ~f shard = + let module J = Yojson.Basic.Util in + let op = J.(member "op" f |> to_int) + |> Opcode.from_int + in + match op with + | DISPATCH -> dispatch ~payload:f shard + | HEARTBEAT -> heartbeat shard + | INVALID_SESSION -> begin + if J.(member "d" f |> to_bool) then + initialize shard + else begin + initialize { shard with session = None; } + end end - end - | RECONNECT -> initialize shard - | HELLO -> initialize ~data:(J.member "d" f) shard - | HEARTBEAT_ACK -> return shard - | opcode -> - print_endline @@ "Invalid Opcode: " ^ Opcode.to_string opcode; - return shard + | RECONNECT -> initialize shard + | HELLO -> initialize ~data:(J.member "d" f) shard + | HEARTBEAT_ACK -> return shard + | opcode -> + print_endline @@ "Invalid Opcode: " ^ Opcode.to_string opcode; + return shard - let rec create ~url ~shards ~token () = - let open Core in - let uri = (url ^ "?v=6&encoding=json") |> Uri.of_string in - let extra_headers = Http.Base.process_request_headers () in - let host = Option.value_exn ~message:"no host in uri" Uri.(host uri) in - let port = - match Uri.port uri, Uri_services.tcp_port_of_uri uri with - | Some p, _ -> p - | None, Some p -> p - | _ -> 443 in - let scheme = Option.value_exn ~message:"no scheme in uri" Uri.(scheme uri) in - let tcp_fun (net_to_ws, ws_to_net) = - let (app_to_ws, write) = Pipe.create () in - let (read, ws_to_app) = Pipe.create () in - let initialized = Ivar.create () in - client - ~initialized - ~extra_headers - ~app_to_ws - ~ws_to_app - ~net_to_ws - ~ws_to_net - uri - >>> ignore; (* TODO this needs to error check and retry with backoff *) - Ivar.read initialized >>| fun () -> - { - pipe = (read, write); - ready = Ivar.create (); - hb = None; - seq = 0; - id = shards; - session = None; - token; - url; - } - in - match Unix.getaddrinfo host (string_of_int port) [] with - | [] -> failwithf "DNS resolution failed for %s" host () - | { ai_addr; _ } :: _ -> - let addr = - match scheme, ai_addr with - | _, ADDR_UNIX path -> `Unix_domain_socket path - | "https", ADDR_INET (h, p) - | "wss", ADDR_INET (h, p) -> - let h = Ipaddr_unix.of_inet_addr h in - `OpenSSL (h, p, Conduit_async.V2.Ssl.Config.create ()) - | _, ADDR_INET (h, p) -> - let h = Ipaddr_unix.of_inet_addr h in - `TCP (h, p) + let rec create ~url ~shards ~token () = + let open Core in + let uri = (url ^ "?v=6&encoding=json") |> Uri.of_string in + let extra_headers = H.Base.process_request_headers () in + let host = Option.value_exn ~message:"no host in uri" Uri.(host uri) in + let port = + match Uri.port uri, Uri_services.tcp_port_of_uri uri with + | Some p, _ -> p + | None, Some p -> p + | _ -> 443 in + let scheme = Option.value_exn ~message:"no scheme in uri" Uri.(scheme uri) in + let tcp_fun (net_to_ws, ws_to_net) = + let (app_to_ws, write) = Pipe.create () in + let (read, ws_to_app) = Pipe.create () in + let initialized = Ivar.create () in + client + ~initialized + ~extra_headers + ~app_to_ws + ~ws_to_app + ~net_to_ws + ~ws_to_net + uri + >>> ignore; (* TODO this needs to error check and retry with backoff *) + Ivar.read initialized >>| fun () -> + { + pipe = (read, write); + ready = Ivar.create (); + hb = None; + seq = 0; + id = shards; + session = None; + token; + url; + } in - Conduit_async.V2.connect addr >>= tcp_fun - and recreate shard = - print_endline "Reconnecting..."; - (match shard.hb with - | Some hb -> Ivar.fill_if_empty hb () - | None -> () - ); - create ~url:(shard.url) ~shards:(shard.id) ~token:(shard.token) () -end + match Unix.getaddrinfo host (string_of_int port) [] with + | [] -> failwithf "DNS resolution failed for %s" host () + | { ai_addr; _ } :: _ -> + let addr = + match scheme, ai_addr with + | _, ADDR_UNIX path -> `Unix_domain_socket path + | "https", ADDR_INET (h, p) + | "wss", ADDR_INET (h, p) -> + let h = Ipaddr_unix.of_inet_addr h in + `OpenSSL (h, p, Conduit_async.V2.Ssl.Config.create ()) + | _, ADDR_INET (h, p) -> + let h = Ipaddr_unix.of_inet_addr h in + `TCP (h, p) + in + Conduit_async.V2.connect addr >>= tcp_fun + and recreate shard = + print_endline "Reconnecting..."; + (match shard.hb with + | Some hb -> Ivar.fill_if_empty hb () + | None -> () + ); + create ~url:(shard.url) ~shards:(shard.id) ~token:(shard.token) () + end -type t = { - shards: (Shard.shard Shard.t) list; -} + type t = { + shards: (Shard.shard Shard.t) list; + } -let start ?count token = - let module J = Yojson.Basic.Util in - Http.get_gateway_bot () >>= fun data -> - let url = J.(member "url" data |> to_string) in - let count = match count with - | Some c -> c - | None -> J.(member "shards" data |> to_int) - in - let shard_list = (0, count) in - let rec ev_loop t = - let (read, _) = t.shard.pipe in - Pipe.read read - >>| fun frame -> - let _ = match parse frame with - | Some f -> begin - handle_frame ~f t.shard - >>> fun shard -> - t.shard <- shard; - end - | None -> t.shard <- recreate t.shard; + let start ?count token = + let module J = Yojson.Basic.Util in + Http.get_gateway_bot () >>= fun data -> + let url = J.(member "url" data |> to_string) in + let count = match count with + | Some c -> c + | None -> J.(member "shards" data |> to_int) + in + let shard_list = (0, count) in + let rec ev_loop t = + let (read, _) = t.shard.pipe in + Pipe.read read + >>| fun frame -> + let _ = match parse frame with + | Some f -> begin + handle_frame ~f t.shard + >>> fun shard -> + t.shard <- shard; + end + | None -> t.shard <- recreate t.shard; + in + t + >>= fun t -> + List.iter ~f:(fun f -> f t.shard) t.binds; + ev_loop t + in + let rec gen_shards l a = + match l with + | (id, total) when id >= total -> return a + | (id, total) -> + Shard.create ~url ~shards:(id, total) ~token () + >>= fun shard -> + let t = { shard; binds = []; } in + ev_loop t >>> ignore; + gen_shards (id+1, total) (t :: a) in - t - >>= fun t -> - List.iter ~f:(fun f -> f t.shard) t.binds; - ev_loop t - in - let rec gen_shards l a = - match l with - | (id, total) when id >= total -> return a - | (id, total) -> - Shard.create ~url ~shards:(id, total) ~token () - >>= fun shard -> - let t = { shard; binds = []; } in - ev_loop t >>> ignore; - gen_shards (id+1, total) (t :: a) - in - gen_shards shard_list [] - >>| fun shards -> - { shards; } + gen_shards shard_list [] + >>| fun shards -> + { shards; } -let set_status sharder status = - Deferred.all @@ List.map ~f:(fun t -> - Shard.set_status ~status t.shard - ) sharder.shards + let set_status sharder status = + Deferred.all @@ List.map ~f:(fun t -> + Shard.set_status ~status t.shard + ) sharder.shards -let set_status_with sharder f = - Deferred.all @@ List.map ~f:(fun t -> - Shard.set_status ~status:(f t.shard) t.shard - ) sharder.shards + let set_status_with sharder f = + Deferred.all @@ List.map ~f:(fun t -> + Shard.set_status ~status:(f t.shard) t.shard + ) sharder.shards -let request_guild_members ~guild ?query ?limit sharder = - Deferred.all @@ List.map ~f:(fun t -> - Shard.request_guild_members ~guild ?query ?limit t.shard - ) sharder.shards + let request_guild_members ~guild ?query ?limit sharder = + Deferred.all @@ List.map ~f:(fun t -> + Shard.request_guild_members ~guild ?query ?limit t.shard + ) sharder.shards +end
\ No newline at end of file |