├── .gitattributes ├── .github └── workflows │ ├── deploy-doc.yml │ └── workflow.yml ├── .gitignore ├── .ocamlformat ├── CHANGES.md ├── LICENSE.md ├── README.md ├── dune-project ├── reddit_api_async.opam ├── reddit_api_async ├── bounded_set.ml ├── bounded_set.mli ├── connection.ml ├── connection.mli ├── dune ├── import.ml ├── iter_comments.ml ├── iter_comments.mli ├── rate_limiter.ml ├── rate_limiter.mli ├── reddit_api_async.ml ├── retry_manager.ml ├── retry_manager.mli ├── stream.ml └── stream.mli ├── reddit_api_kernel.opam ├── reddit_api_kernel ├── comment_response.ml ├── comment_response.mli ├── dune ├── endpoint.ml ├── endpoint.mli ├── id36.ml ├── id36.mli ├── id36_intf.ml ├── inbox_item.ml ├── inbox_item.mli ├── json_object.ml ├── json_object.mli ├── json_object_intf.ml ├── karma_list.ml ├── karma_list.mli ├── listing.ml ├── listing.mli ├── mod_action.ml ├── mod_action.mli ├── moderator_report.ml ├── moderator_report.mli ├── modmail.ml ├── modmail.mli ├── rate_limiter_state_machine.ml ├── rate_limiter_state_machine.mli ├── reddit_api_kernel.ml ├── relationship.ml ├── relationship.mli ├── relationship_intf.ml ├── stylesheet.ml ├── stylesheet.mli ├── submit_text.ml ├── submit_text.mli ├── subreddit_name.ml ├── subreddit_name.mli ├── subreddit_rules.ml ├── subreddit_rules.mli ├── subreddit_settings.ml ├── subreddit_settings.mli ├── subreddit_traffic.ml ├── subreddit_traffic.mli ├── thing.ml ├── thing.mli ├── thing_intf.ml ├── thing_kind.ml ├── thing_kind.mli ├── uri_with_string_sexp.ml ├── uri_with_string_sexp.mli ├── user_list.ml ├── user_list.mli ├── username.ml ├── username.mli ├── wiki_page.ml └── wiki_page.mli └── test ├── cassettes ├── add_wiki_editor.sexp ├── ban__long_message.sexp ├── ban__wrong_subreddit.sexp ├── banned.sexp ├── best.sexp ├── block_author.sexp ├── blocked.sexp ├── collapse_message.sexp ├── comment_fields.sexp ├── comment_replies.sexp ├── comments.sexp ├── compose_message.sexp ├── contributors.sexp ├── create_modmail_conversation.sexp ├── delete.sexp ├── delete_subreddit_banner.sexp ├── delete_subreddit_header.sexp ├── delete_subreddit_icon.sexp ├── delete_subreddit_image.sexp ├── distinguish.sexp ├── edit.sexp ├── edited.sexp ├── friends.sexp ├── get_subreddits.sexp ├── hot.sexp ├── hot__multiple_subreddits.sexp ├── ignore_reports.sexp ├── inbox.sexp ├── info.sexp ├── info__by_subreddit_name.sexp ├── leavecontributor.sexp ├── leavemoderator.sexp ├── link_fields__self_contents.sexp ├── link_fields__url_contents.sexp ├── links_by_id.sexp ├── list_subreddits.sexp ├── log.sexp ├── me.sexp ├── messaging.sexp ├── moderators.sexp ├── modqueue.sexp ├── more_comments.sexp ├── muted.sexp ├── oauth2_expired_access_token.sexp ├── oauth2_refresh_token.sexp ├── oauth2_refresh_token__bad_token.sexp ├── oauth2_refresh_token_insufficient_scope.sexp ├── random.sexp ├── read_message.sexp ├── remove.sexp ├── remove_wiki_editor.sexp ├── reply_modmail_conversation.sexp ├── report.sexp ├── reports.sexp ├── revert_wiki_page.sexp ├── save.sexp ├── search.sexp ├── search__all.sexp ├── search__subreddits.sexp ├── search_subreddits_by_name.sexp ├── search_subreddits_by_title_and_description.sexp ├── search_users.sexp ├── select_flair.sexp ├── send_replies.sexp ├── sent.sexp ├── set_contest_mode.sexp ├── set_subreddit_sticky.sexp ├── set_subreddit_stylesheet.sexp ├── set_suggested_sort.sexp ├── set_wiki_permissions.sexp ├── spam.sexp ├── spoiler.sexp ├── stylesheet.sexp ├── submit.sexp ├── submit__crosspost.sexp ├── submit_text.sexp ├── subreddit_autocomplete.sexp ├── subreddit_fields.sexp ├── subreddit_rules.sexp ├── subreddit_settings.sexp ├── subreddit_sticky.sexp ├── subreddit_traffic.sexp ├── subreddit_wiki_revisions.sexp ├── subscribe__by_id.sexp ├── subscribe__by_name.sexp ├── toggle_wiki_revision_visibility.sexp ├── trophies.sexp ├── trusted.sexp ├── uncollapse_message.sexp ├── unignore_reports.sexp ├── unmoderated.sexp ├── unread.sexp ├── unread_message.sexp ├── unsave.sexp ├── unspoiler.sexp ├── user_fields.sexp ├── user_overview.sexp ├── user_trophies.sexp ├── user_trophies__nonexistent_user.sexp ├── user_upvoted.sexp ├── userless_confidential.sexp ├── userless_public.sexp ├── vote.sexp ├── wiki_banned.sexp ├── wiki_contributors.sexp ├── wiki_discussions.sexp ├── wiki_page_revisions.sexp ├── wiki_pages.sexp └── wiki_permissions.sexp ├── dune ├── import.ml ├── test_account.ml ├── test_api.ml ├── test_comment_fields.ml ├── test_comment_page.ml ├── test_delete.ml ├── test_error_handling.ml ├── test_hot.ml ├── test_id36.ml ├── test_info.ml ├── test_link_fields.ml ├── test_links_and_comments.ml ├── test_listings.ml ├── test_messages.ml ├── test_moderation.ml ├── test_modmail.ml ├── test_oauth2_refresh_token.ml ├── test_oauth2_userless.ml ├── test_rate_limiter.ml ├── test_report.ml ├── test_search.ml ├── test_select_flair.ml ├── test_set_subreddit_sticky.ml ├── test_set_suggested_sort.ml ├── test_submit.ml ├── test_subreddit_fields.ml ├── test_subreddit_name.ml ├── test_subreddits.ml ├── test_trophies.ml ├── test_user_fields.ml ├── test_username.ml ├── test_users.ml └── test_wiki_page.ml /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ml linguist-language=OCaml 2 | *.mli linguist-language=OCaml 3 | *.sexp -linguist-detectable 4 | -------------------------------------------------------------------------------- /.github/workflows/deploy-doc.yml: -------------------------------------------------------------------------------- 1 | name: Deploy odoc to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: read-all 9 | 10 | concurrency: 11 | group: deploy-odoc 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | deploy-odoc: 16 | name: Deploy odoc to GitHub Pages 17 | 18 | environment: 19 | name: github-pages 20 | url: ${{ steps.deployment.outputs.page_url }} 21 | 22 | permissions: 23 | contents: read 24 | id-token: write 25 | pages: write 26 | 27 | runs-on: ubuntu-latest 28 | 29 | steps: 30 | - name: Checkout tree 31 | uses: actions/checkout@v4 32 | 33 | - name: Set-up OCaml 34 | uses: ocaml/setup-ocaml@v2 35 | with: 36 | ocaml-compiler: "5.1" 37 | 38 | - name: Install dependencies 39 | run: opam install . --deps-only --with-doc 40 | 41 | - name: Build documentation 42 | run: opam exec -- dune build @doc 43 | 44 | - name: Set-up Pages 45 | uses: actions/configure-pages@v4 46 | 47 | - name: Upload artifact 48 | uses: actions/upload-pages-artifact@v2 49 | with: 50 | path: _build/default/_doc/_html 51 | 52 | - name: Deploy odoc to GitHub Pages 53 | id: deployment 54 | uses: actions/deploy-pages@v3 -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Main workflow 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v3 14 | 15 | - name: Use OCaml 4.14.x 16 | uses: avsm/setup-ocaml@v2 17 | with: 18 | ocaml-compiler: 4.14.x 19 | dune-cache: true 20 | 21 | - run: opam install . --deps-only --with-test 22 | 23 | - run: opam exec -- dune build 24 | 25 | - run: opam exec -- dune runtest 26 | 27 | lint-doc: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v3 32 | 33 | - name: Use OCaml 4.14.x 34 | uses: ocaml/setup-ocaml@v2 35 | with: 36 | ocaml-compiler: 4.14.x 37 | dune-cache: true 38 | 39 | - name: Lint doc 40 | uses: ocaml/setup-ocaml/lint-doc@v2 41 | 42 | lint-fmt: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - name: Checkout code 46 | uses: actions/checkout@v3 47 | 48 | - name: Use OCaml 4.14.x 49 | uses: ocaml/setup-ocaml@v2 50 | with: 51 | ocaml-compiler: 4.14.x 52 | dune-cache: true 53 | 54 | - name: Lint fmt 55 | uses: ocaml/setup-ocaml/lint-fmt@v2 56 | 57 | lint-opam: 58 | runs-on: ubuntu-latest 59 | steps: 60 | - name: Checkout code 61 | uses: actions/checkout@v3 62 | 63 | - name: Use OCaml 4.14.x 64 | uses: ocaml/setup-ocaml@v2 65 | with: 66 | ocaml-compiler: 4.14.x 67 | dune-cache: true 68 | 69 | - name: Lint opam 70 | uses: ocaml/setup-ocaml/lint-opam@v2 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | _opam/ 3 | .merlin 4 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | version = 0.21.0 2 | profile = janestreet 3 | ocp-indent-compat = true 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 Levi Roth 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.9) 2 | 3 | (generate_opam_files true) 4 | 5 | (name reddit_api) 6 | 7 | (version 0.2.1) 8 | 9 | (source (github leviroth/ocaml-reddit-api)) 10 | 11 | (license MIT) 12 | 13 | (authors "Levi Roth") 14 | 15 | (maintainers levimroth@gmail.com) 16 | 17 | (package 18 | (name reddit_api_kernel) 19 | (synopsis "OCaml types for Reddit's API") 20 | (depends 21 | (cohttp (>= 5.0.0)) 22 | (core_kernel (>= v0.16.0)) 23 | (core (>= v0.16.0)) 24 | (expect_test_helpers_async (and (>= v0.16.0) :with-test)) 25 | (jsonaf (>= v0.16.0)) 26 | (odoc :with-doc) 27 | (ppx_jane (>= v0.16.0)) 28 | (ppx_jsonaf_conv (>= v0.16.0)))) 29 | 30 | (package 31 | (name reddit_api_async) 32 | (synopsis "Async connection and utility functions for Reddit's API") 33 | (depends 34 | (async (>= v0.16.0)) 35 | (async_ssl (>= v0.16.0)) 36 | (cohttp-async (>= 5.0.0)) 37 | (core (>= v0.16.0)) 38 | (expect_test_helpers_async (and (>= v0.16.0) :with-test)) 39 | (jsonaf (>= v0.16.0)) 40 | (ppx_jane (>= v0.16.0)) 41 | (ppx_jsonaf_conv (>= v0.16.0)) 42 | (reddit_api_kernel (= :version)) 43 | (sequencer_table (>= v0.16.0)))) 44 | -------------------------------------------------------------------------------- /reddit_api_async.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | version: "0.2.1" 4 | synopsis: "Async connection and utility functions for Reddit's API" 5 | maintainer: ["levimroth@gmail.com"] 6 | authors: ["Levi Roth"] 7 | license: "MIT" 8 | homepage: "https://github.com/leviroth/ocaml-reddit-api" 9 | bug-reports: "https://github.com/leviroth/ocaml-reddit-api/issues" 10 | depends: [ 11 | "dune" {>= "2.9"} 12 | "async" {>= "v0.16.0"} 13 | "async_ssl" {>= "v0.16.0"} 14 | "cohttp-async" {>= "5.0.0"} 15 | "core" {>= "v0.16.0"} 16 | "expect_test_helpers_async" {>= "v0.16.0" & with-test} 17 | "jsonaf" {>= "v0.16.0"} 18 | "ppx_jane" {>= "v0.16.0"} 19 | "ppx_jsonaf_conv" {>= "v0.16.0"} 20 | "reddit_api_kernel" {= version} 21 | "sequencer_table" {>= "v0.16.0"} 22 | "odoc" {with-doc} 23 | ] 24 | build: [ 25 | ["dune" "subst"] {dev} 26 | [ 27 | "dune" 28 | "build" 29 | "-p" 30 | name 31 | "-j" 32 | jobs 33 | "--promote-install-files=false" 34 | "@install" 35 | "@runtest" {with-test} 36 | "@doc" {with-doc} 37 | ] 38 | ["dune" "install" "-p" name "--create-install-files" name] 39 | ] 40 | dev-repo: "git+https://github.com/leviroth/ocaml-reddit-api.git" 41 | -------------------------------------------------------------------------------- /reddit_api_async/bounded_set.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Make (Hashable : Hashtbl.Key_plain) = struct 4 | type t = 5 | { capacity : int 6 | ; hash_queue : (Hashable.t, unit) Hash_queue.t 7 | } 8 | 9 | let create ~capacity = 10 | { capacity 11 | ; hash_queue = Hash_queue.create (Hashtbl.Hashable.of_key (module Hashable)) 12 | } 13 | ;; 14 | 15 | let mem { hash_queue; _ } value = 16 | match Hash_queue.lookup_and_move_to_back hash_queue value with 17 | | Some () -> true 18 | | None -> false 19 | ;; 20 | 21 | let add ({ hash_queue; capacity } as t) value = 22 | match mem t value with 23 | | true -> () 24 | | false -> 25 | Hash_queue.enqueue_back_exn hash_queue value (); 26 | (match Hash_queue.length hash_queue > capacity with 27 | | false -> () 28 | | true -> Hash_queue.drop hash_queue `front) 29 | ;; 30 | end 31 | -------------------------------------------------------------------------------- /reddit_api_async/bounded_set.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Make (Hashable : Hashtbl.Key_plain) : sig 4 | type t 5 | 6 | val create : capacity:int -> t 7 | val add : t -> Hashable.t -> unit 8 | val mem : t -> Hashable.t -> bool 9 | end 10 | -------------------------------------------------------------------------------- /reddit_api_async/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name reddit_api_async) 3 | (public_name reddit_api_async) 4 | (libraries async core cohttp-async reddit_api_kernel sequencer_table) 5 | (inline_tests) 6 | (preprocess 7 | (pps ppx_jane))) 8 | -------------------------------------------------------------------------------- /reddit_api_async/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | 4 | let log = Log.create ~level:`Error ~output:[] ~on_error:`Raise () 5 | -------------------------------------------------------------------------------- /reddit_api_async/iter_comments.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | open Reddit_api_kernel 5 | open Thing 6 | 7 | let retry_or_log_unexpected retry_manager here endpoint = 8 | match%bind Retry_manager.call retry_manager endpoint with 9 | | Ok v -> return (Some v) 10 | | Error error -> 11 | [%log.error 12 | log 13 | "Unexpected response from Reddit" 14 | (here : Source_code_position.t) 15 | (error : Retry_manager.Permanent_error.t)]; 16 | return None 17 | ;; 18 | 19 | let children item ~retry_manager ~link = 20 | match item with 21 | | `Comment comment -> return (Comment.replies comment) 22 | | `More_comments more_comments -> 23 | (match More_comments.details more_comments with 24 | | By_children more_comments -> 25 | retry_or_log_unexpected 26 | retry_manager 27 | [%here] 28 | (Endpoint.more_children ~link ~more_comments ~sort:New ()) 29 | | By_parent parent -> 30 | retry_or_log_unexpected 31 | retry_manager 32 | [%here] 33 | (Endpoint.comments ~comment:parent () ~link) 34 | >>| Option.map 35 | ~f:(fun ({ comment_forest; link = _ } as response : Comment_response.t) -> 36 | match comment_forest with 37 | | [ `Comment comment ] -> Comment.replies comment 38 | | _ -> 39 | raise_s 40 | [%message 41 | "Expected single comment at root of response" 42 | (link : Link.Id.t) 43 | (parent : Comment.Id.t) 44 | (response : Comment_response.t)])) 45 | >>| Option.value ~default:[] 46 | ;; 47 | 48 | let iter_comments 49 | retry_manager 50 | ~comment_response:({ link; comment_forest } : Comment_response.t) 51 | = 52 | let link = Link.id link in 53 | let queue = Queue.of_list comment_forest in 54 | Pipe.create_reader ~close_on_exception:false (fun writer -> 55 | Deferred.repeat_until_finished () (fun () -> 56 | match Pipe.is_closed writer with 57 | | true -> return (`Finished ()) 58 | | false -> 59 | (match Queue.dequeue queue with 60 | | None -> return (`Finished ()) 61 | | Some item -> 62 | let%bind () = 63 | match item with 64 | | `More_comments _ -> return () 65 | | `Comment comment -> Pipe.write_if_open writer comment 66 | in 67 | let%bind children = children item ~retry_manager ~link in 68 | Queue.enqueue_all queue children; 69 | return (`Repeat ())))) 70 | ;; 71 | -------------------------------------------------------------------------------- /reddit_api_async/iter_comments.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open Reddit_api_kernel 4 | 5 | val iter_comments 6 | : Retry_manager.t 7 | -> comment_response:Comment_response.t 8 | -> Thing.Comment.t Pipe.Reader.t 9 | -------------------------------------------------------------------------------- /reddit_api_async/rate_limiter.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | module Rate_limiter_state_machine = Reddit_api_kernel.Rate_limiter_state_machine 4 | 5 | type t = 6 | { mutable state : Rate_limiter_state_machine.t 7 | ; response_received : (unit, read_write) Bvar.t 8 | ; time_source : (Time_source.t[@sexp.opaque]) 9 | } 10 | [@@deriving sexp_of] 11 | 12 | let of_state_machine state time_source = 13 | let response_received = Bvar.create () in 14 | { state; response_received; time_source } 15 | ;; 16 | 17 | let is_ready t = 18 | let now = Time_source.now t.time_source in 19 | match Rate_limiter_state_machine.wait_until t.state with 20 | | Now -> true 21 | | Check_after_receiving_response -> false 22 | | After time -> Time_ns.( >= ) now time 23 | ;; 24 | 25 | let wait_until_ready t = 26 | Deferred.repeat_until_finished () (fun () -> 27 | match Rate_limiter_state_machine.wait_until t.state with 28 | | Now -> return (`Finished ()) 29 | | After time -> 30 | (match Time_ns.( >= ) (Time_source.now t.time_source) time with 31 | | true -> return (`Finished ()) 32 | | false -> 33 | let%bind () = Time_source.at t.time_source time in 34 | return (`Repeat ())) 35 | | Check_after_receiving_response -> 36 | let%bind () = Bvar.wait t.response_received in 37 | return (`Repeat ())) 38 | ;; 39 | 40 | let permit_request t = 41 | Deferred.repeat_until_finished () (fun () -> 42 | let%bind () = wait_until_ready t in 43 | match is_ready t with 44 | | false -> return (`Repeat ()) 45 | | true -> 46 | t.state 47 | <- Rate_limiter_state_machine.sent_request_unchecked 48 | t.state 49 | ~now:(Time_source.now t.time_source); 50 | return (`Finished ())) 51 | ;; 52 | 53 | let notify_response t response = 54 | t.state <- Rate_limiter_state_machine.received_response t.state response; 55 | Bvar.broadcast t.response_received () 56 | ;; 57 | -------------------------------------------------------------------------------- /reddit_api_async/rate_limiter.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | 4 | type t [@@deriving sexp_of] 5 | 6 | val of_state_machine 7 | : Reddit_api_kernel.Rate_limiter_state_machine.t 8 | -> Time_source.t 9 | -> t 10 | 11 | val permit_request : t -> unit Deferred.t 12 | val notify_response : t -> Cohttp.Response.t -> unit 13 | val is_ready : t -> bool 14 | val wait_until_ready : t -> unit Deferred.t 15 | -------------------------------------------------------------------------------- /reddit_api_async/reddit_api_async.ml: -------------------------------------------------------------------------------- 1 | include Reddit_api_kernel 2 | module Connection = Connection 3 | module Iter_comments = Iter_comments 4 | module Rate_limiter = Rate_limiter 5 | module Retry_manager = Retry_manager 6 | module Stream = Stream 7 | 8 | module Logging = struct 9 | let log = Import.log 10 | end 11 | -------------------------------------------------------------------------------- /reddit_api_async/stream.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open Reddit_api_kernel 4 | 5 | val stream 6 | : (module Hashtbl.Key_plain with type t = 'id) 7 | -> Connection.t 8 | -> get_listing:(before:'id option -> limit:int -> 'thing list Endpoint.t) 9 | -> get_before_parameter:('thing -> 'id) 10 | -> ('thing, Endpoint.Error.t Connection.Error.t) Result.t Pipe.Reader.t 11 | -------------------------------------------------------------------------------- /reddit_api_kernel.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | version: "0.2.1" 4 | synopsis: "OCaml types for Reddit's API" 5 | maintainer: ["levimroth@gmail.com"] 6 | authors: ["Levi Roth"] 7 | license: "MIT" 8 | homepage: "https://github.com/leviroth/ocaml-reddit-api" 9 | bug-reports: "https://github.com/leviroth/ocaml-reddit-api/issues" 10 | depends: [ 11 | "dune" {>= "2.9"} 12 | "cohttp" {>= "5.0.0"} 13 | "core_kernel" {>= "v0.16.0"} 14 | "core" {>= "v0.16.0"} 15 | "expect_test_helpers_async" {>= "v0.16.0" & with-test} 16 | "jsonaf" {>= "v0.16.0"} 17 | "odoc" {with-doc} 18 | "ppx_jane" {>= "v0.16.0"} 19 | "ppx_jsonaf_conv" {>= "v0.16.0"} 20 | ] 21 | build: [ 22 | ["dune" "subst"] {dev} 23 | [ 24 | "dune" 25 | "build" 26 | "-p" 27 | name 28 | "-j" 29 | jobs 30 | "--promote-install-files=false" 31 | "@install" 32 | "@runtest" {with-test} 33 | "@doc" {with-doc} 34 | ] 35 | ["dune" "install" "-p" name "--create-install-files" name] 36 | ] 37 | dev-repo: "git+https://github.com/leviroth/ocaml-reddit-api.git" 38 | -------------------------------------------------------------------------------- /reddit_api_kernel/comment_response.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Thing 3 | 4 | type t = 5 | { link : Thing.Link.t 6 | ; comment_forest : [ `Comment of Comment.t | `More_comments of More_comments.t ] list 7 | } 8 | [@@deriving sexp] 9 | -------------------------------------------------------------------------------- /reddit_api_kernel/comment_response.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Thing 3 | 4 | type t = 5 | { link : Thing.Link.t 6 | ; comment_forest : [ `Comment of Comment.t | `More_comments of More_comments.t ] list 7 | } 8 | [@@deriving sexp] 9 | -------------------------------------------------------------------------------- /reddit_api_kernel/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name reddit_api_kernel) 3 | (public_name reddit_api_kernel) 4 | (libraries core cohttp core_kernel.uuid jsonaf) 5 | (preprocess 6 | (pps ppx_jane ppx_jsonaf_conv))) 7 | -------------------------------------------------------------------------------- /reddit_api_kernel/id36.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Id36_intf 3 | 4 | module M = struct 5 | type t = Int63.t [@@deriving compare, hash, bin_io] 6 | 7 | let of_int63 = Fn.id 8 | let to_int63 = Fn.id 9 | let base = Int63.of_int 36 10 | 11 | let to_string i = 12 | let rec of_int i acc = 13 | match Int63.equal i Int63.zero with 14 | | true -> 15 | (match acc with 16 | | [] -> "0" 17 | | _ -> String.of_char_list acc) 18 | | false -> 19 | let current_place = Option.value_exn (Int63.to_int Int63.O.(i % base)) in 20 | let character = 21 | let char_from_base base offset = Char.to_int base + offset |> Char.of_int_exn in 22 | match current_place < 10 with 23 | | true -> char_from_base '0' current_place 24 | | false -> char_from_base 'a' (current_place - 10) 25 | in 26 | of_int Int63.O.(i / base) (character :: acc) 27 | in 28 | of_int i [] 29 | ;; 30 | 31 | let of_string t : t = 32 | let convert_char c = 33 | let convert_to_offset base_char = Char.to_int c - Char.to_int base_char in 34 | Int63.of_int 35 | (match Char.is_alpha c with 36 | | true -> convert_to_offset 'a' + 10 37 | | false -> convert_to_offset '0') 38 | in 39 | String.fold t ~init:Int63.zero ~f:(fun acc c -> 40 | Int63.O.((acc * base) + convert_char c)) 41 | ;; 42 | 43 | let sexp_of_t t = to_string t |> sexp_of_string 44 | let t_of_sexp sexp = String.t_of_sexp sexp |> of_string 45 | let module_name = "Id36" 46 | end 47 | 48 | include M 49 | include Identifiable.Make (M) 50 | include Jsonaf.Jsonafable.Of_stringable (M) 51 | -------------------------------------------------------------------------------- /reddit_api_kernel/id36.mli: -------------------------------------------------------------------------------- 1 | include Id36_intf.Id36 (** @inline *) 2 | -------------------------------------------------------------------------------- /reddit_api_kernel/id36_intf.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module type S = sig 4 | type t [@@deriving sexp] 5 | 6 | include Identifiable.S with type t := t 7 | include Jsonaf.Jsonafable.S with type t := t 8 | 9 | val of_int63 : Int63.t -> t 10 | val to_int63 : t -> Int63.t 11 | end 12 | 13 | module type Id36 = sig 14 | module type S = S 15 | 16 | include S 17 | end 18 | -------------------------------------------------------------------------------- /reddit_api_kernel/inbox_item.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Comment = struct 4 | include Json_object.Utils 5 | 6 | include Json_object.Make_kinded_simple (struct 7 | let kind = "t1" 8 | end) 9 | 10 | module Type = struct 11 | type t = 12 | | Comment_reply 13 | | Link_reply 14 | [@@deriving sexp] 15 | end 16 | 17 | let id = required_field "id" (string >> Thing.Comment.Id.of_string) 18 | 19 | let body t markup = 20 | let field = 21 | match markup with 22 | | `markdown -> "body" 23 | | `HTML -> "body_html" 24 | in 25 | required_field field string t 26 | ;; 27 | 28 | let author = required_field "author" (string >> Username.of_string_or_deleted) 29 | let subreddit = required_field "subreddit" subreddit_name 30 | let creation_time = required_field "created_utc" time_sec_since_epoch 31 | let score = required_field "score" int 32 | 33 | let parent_id = 34 | required_field "parent_id" (fun json -> 35 | let id_string = string json in 36 | match Thing.Fullname.of_string id_string with 37 | | (`Comment _ | `Link _) as v -> v 38 | | _ -> 39 | raise_s 40 | [%message "Unexpected Inbox.Comment.parent_id kind" (id_string : string)]) 41 | ;; 42 | 43 | let new_ = required_field "new" bool 44 | 45 | let type_ = 46 | required_field "type" (fun json : Type.t -> 47 | match string json with 48 | | "post_reply" -> Link_reply 49 | | "comment_reply" -> Comment_reply 50 | | type_ -> raise_s [%message "Unrecognized Inbox.Comment.t type" (type_ : string)]) 51 | ;; 52 | 53 | let link_id = required_field "context" (uri >> Thing.Link.Id.of_uri) 54 | let link_title = required_field "link_title" string 55 | let num_comments_in_thread = required_field "num_comments" int 56 | end 57 | 58 | type t = 59 | | Message of Thing.Message.t 60 | | Comment of Comment.t 61 | [@@deriving sexp] 62 | 63 | let t_of_jsonaf json = 64 | let kind = Jsonaf.member_exn "kind" json |> Jsonaf.string_exn in 65 | let data = Jsonaf.member_exn "data" json in 66 | match kind with 67 | | "t1" -> Comment ([%of_jsonaf: Comment.t] data) 68 | | "t4" -> Message ([%of_jsonaf: Thing.Message.t] data) 69 | | _ -> raise_s [%message "Unrecognized Inbox_item kind" (kind : string)] 70 | ;; 71 | -------------------------------------------------------------------------------- /reddit_api_kernel/inbox_item.mli: -------------------------------------------------------------------------------- 1 | (** An inbox item is either a {!type:Thing.Message.t} or else a representation 2 | of a comment with different fields from a {!Thing.Comment.t}. 3 | *) 4 | 5 | open! Core 6 | 7 | module Comment : sig 8 | module Type : sig 9 | type t = 10 | | Comment_reply 11 | | Link_reply 12 | [@@deriving sexp] 13 | end 14 | 15 | type t [@@deriving sexp] 16 | 17 | include Json_object.S_with_fields with type t := t 18 | include Json_object.S_with_kind with type t := t 19 | 20 | val id : t -> Thing.Comment.Id.t 21 | val body : t -> [ `markdown | `HTML ] -> string 22 | val author : t -> Username.t option 23 | val subreddit : t -> Subreddit_name.t 24 | val creation_time : t -> Time_ns.t 25 | val score : t -> int 26 | val parent_id : t -> [ `Comment of Thing.Comment.Id.t | `Link of Thing.Link.Id.t ] 27 | val new_ : t -> bool 28 | val type_ : t -> Type.t 29 | val link_id : t -> Thing.Link.Id.t 30 | val link_title : t -> string 31 | val num_comments_in_thread : t -> int 32 | end 33 | 34 | type t = 35 | | Message of Thing.Message.t 36 | | Comment of Comment.t 37 | [@@deriving sexp, of_jsonaf] 38 | -------------------------------------------------------------------------------- /reddit_api_kernel/json_object.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Json_object_intf 3 | 4 | module Utils = struct 5 | type t = Jsonaf.t Map.M(String).t [@@deriving sexp] 6 | 7 | let field_map = Fn.id 8 | let t_of_jsonaf json = Jsonaf.assoc_list_exn json |> Map.of_alist_exn (module String) 9 | let jsonaf_of_t t = `Object (Map.to_alist t) 10 | let get_field = Map.find 11 | 12 | let get_field_exn t field = 13 | match Map.find t field with 14 | | Some value -> value 15 | | None -> raise_s [%message "Missing JSON field" (t : t) (field : string)] 16 | ;; 17 | 18 | let optional_field name convert t = 19 | match Map.find t name with 20 | | None | Some `Null -> None 21 | | Some v -> Some (convert v) 22 | ;; 23 | 24 | let required_field name convert t = convert (get_field_exn t name) 25 | let ( >> ) f g x = g (f x) 26 | 27 | let or_null f json = 28 | match json with 29 | | `Null -> None 30 | | json -> Some (f json) 31 | ;; 32 | 33 | let int = Jsonaf.int_exn 34 | let float = Jsonaf.float_exn 35 | let bool = Jsonaf.bool_exn 36 | let string = Jsonaf.string_exn 37 | let username = string >> Username.of_string 38 | let subreddit_name = string >> Subreddit_name.of_string 39 | let time_sec_since_epoch = float >> Time_ns.Span.of_sec >> Time_ns.of_span_since_epoch 40 | let uri = string >> Uri.of_string 41 | end 42 | 43 | include Utils 44 | 45 | module Make_kinded (Param : Kinded_param) = struct 46 | include Param 47 | 48 | let t_of_jsonaf json = 49 | match Jsonaf.member "kind" json with 50 | | None -> Param.of_data_field json 51 | | Some (`String kind) -> 52 | (match String.equal Param.kind kind with 53 | | true -> Param.of_data_field (Jsonaf.member_exn "data" json) 54 | | false -> 55 | raise_s 56 | [%message 57 | "Unexpected JSON object kind" 58 | ~expected:(Param.kind : string) 59 | (json : Jsonaf.t)]) 60 | | Some kind -> 61 | raise_s 62 | [%message "JSON object kind is not a string" (kind : Jsonaf.t) (json : Jsonaf.t)] 63 | ;; 64 | 65 | let jsonaf_of_t t = 66 | `Object [ "kind", `String Param.kind; "data", Param.to_data_field t ] 67 | ;; 68 | end 69 | 70 | module Make_kinded_simple (Param : sig 71 | val kind : string 72 | end) = 73 | Make_kinded (struct 74 | type t = Utils.t [@@deriving sexp_of] 75 | 76 | let kind = Param.kind 77 | let of_data_field = [%of_jsonaf: t] 78 | let to_data_field = [%jsonaf_of: t] 79 | end) 80 | -------------------------------------------------------------------------------- /reddit_api_kernel/json_object.mli: -------------------------------------------------------------------------------- 1 | include Json_object_intf.Json_object (** @inline *) 2 | -------------------------------------------------------------------------------- /reddit_api_kernel/json_object_intf.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module type S = sig 4 | type t [@@deriving sexp_of] 5 | 6 | include Jsonaf.Jsonafable.S with type t := t 7 | end 8 | 9 | module type S_with_fields = sig 10 | include S 11 | 12 | val get_field : t -> string -> Jsonaf.t option 13 | val get_field_exn : t -> string -> Jsonaf.t 14 | val field_map : t -> Jsonaf.t Map.M(String).t 15 | end 16 | 17 | module type S_with_kind = sig 18 | include S 19 | 20 | val kind : string 21 | end 22 | 23 | module type Kinded_param = sig 24 | type t [@@deriving sexp_of] 25 | 26 | val of_data_field : Jsonaf.t -> t 27 | val to_data_field : t -> Jsonaf.t 28 | val kind : string 29 | end 30 | 31 | module type Json_object = sig 32 | module type S = S 33 | module type S_with_fields = S_with_fields 34 | module type S_with_kind = S_with_kind 35 | 36 | module Utils : sig 37 | include S_with_fields with type t = Jsonaf.t Map.M(String).t 38 | include Sexpable.S with type t := t 39 | 40 | val optional_field : string -> (Jsonaf.t -> 'a) -> t -> 'a option 41 | val required_field : string -> (Jsonaf.t -> 'a) -> t -> 'a 42 | val ( >> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c 43 | val or_null : (Jsonaf.t -> 'a) -> Jsonaf.t -> 'a option 44 | val int : Jsonaf.t -> int 45 | val float : Jsonaf.t -> float 46 | val bool : Jsonaf.t -> bool 47 | val string : Jsonaf.t -> string 48 | val username : Jsonaf.t -> Username.t 49 | val subreddit_name : Jsonaf.t -> Subreddit_name.t 50 | val time_sec_since_epoch : Jsonaf.t -> Time_ns.t 51 | val uri : Jsonaf.t -> Uri.t 52 | end 53 | 54 | module Make_kinded (Param : Kinded_param) : S_with_kind with type t := Param.t 55 | 56 | module Make_kinded_simple (Param : sig 57 | val kind : string 58 | end) : S_with_kind with type t := Utils.t 59 | end 60 | -------------------------------------------------------------------------------- /reddit_api_kernel/karma_list.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Entry = struct 4 | include Json_object.Utils 5 | 6 | let t_of_jsonaf json = 7 | match Jsonaf.assoc_list json with 8 | | Some alist -> Map.of_alist_exn (module String) alist 9 | | None -> raise_s [%message "Invalid [Karma_list.Entry] JSON" (json : Jsonaf.t)] 10 | ;; 11 | 12 | let jsonaf_of_t t = `Object (Map.to_alist t) 13 | let subreddit = required_field "sr" subreddit_name 14 | let link_karma = required_field "link_karma" int 15 | let comment_karma = required_field "comment_karma" int 16 | end 17 | 18 | type t = Entry.t list [@@deriving sexp] 19 | 20 | include Json_object.Make_kinded (struct 21 | type nonrec t = t [@@deriving sexp_of] 22 | 23 | let of_data_field json = 24 | match Jsonaf.list json with 25 | | Some entries -> List.map entries ~f:[%of_jsonaf: Entry.t] 26 | | None -> raise_s [%message "Invalid [Karma_list] JSON" (json : Jsonaf.t)] 27 | ;; 28 | 29 | let to_data_field t = `Array (List.map t ~f:[%jsonaf_of: Entry.t]) 30 | let kind = "KarmaList" 31 | end) 32 | -------------------------------------------------------------------------------- /reddit_api_kernel/karma_list.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Entry : sig 4 | type t [@@deriving sexp, jsonaf] 5 | 6 | val subreddit : t -> Subreddit_name.t 7 | val link_karma : t -> int 8 | val comment_karma : t -> int 9 | end 10 | 11 | type t = Entry.t list [@@deriving sexp] 12 | 13 | include Json_object.S_with_kind with type t := t 14 | -------------------------------------------------------------------------------- /reddit_api_kernel/listing.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | module Page_id = String 3 | 4 | module Pagination = struct 5 | type t = 6 | | Before of Page_id.t 7 | | After of Page_id.t 8 | [@@deriving sexp] 9 | end 10 | 11 | type 'a t = 12 | { children : 'a list 13 | ; after : Page_id.t option 14 | } 15 | [@@deriving sexp, fields] 16 | 17 | let map t ~f = { t with children = List.map t.children ~f } 18 | 19 | let t_of_jsonaf convert_element json = 20 | let fail s = raise_s [%message s (json : Jsonaf.t)] in 21 | let assoc = 22 | match Jsonaf.assoc_list json with 23 | | Some list -> list 24 | | None -> fail "Expected JSON map when creating [Listing.t]" 25 | in 26 | let data = 27 | match List.Assoc.find assoc "data" ~equal:String.equal with 28 | | Some data -> data 29 | | None -> fail "Missing field \"data\"" 30 | in 31 | let data = 32 | match Jsonaf.assoc_list data with 33 | | Some list -> list 34 | | None -> fail "Expected \"data\" to be an object" 35 | in 36 | let data = 37 | match Map.of_alist (module String) data with 38 | | `Ok map -> map 39 | | `Duplicate_key key -> fail (sprintf "Duplicate key: \"%s\"" key) 40 | in 41 | let children = 42 | match Map.find data "children" with 43 | | Some (`Array l) -> l 44 | | Some _ -> fail "Expected \"children\" to be a list" 45 | | None -> fail "Missing key \"children\"" 46 | in 47 | let children = List.map children ~f:convert_element in 48 | let after = 49 | match Map.find data "after" with 50 | | None | Some `Null -> None 51 | | Some (`String s) -> Some (Page_id.of_string s) 52 | | Some _ -> fail "Expected \"after\" to be a string" 53 | in 54 | { children; after } 55 | ;; 56 | -------------------------------------------------------------------------------- /reddit_api_kernel/listing.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Page_id : sig 4 | type t [@@deriving sexp] 5 | 6 | include Stringable with type t := t 7 | end 8 | 9 | module Pagination : sig 10 | type t = 11 | | Before of Page_id.t 12 | | After of Page_id.t 13 | [@@deriving sexp] 14 | end 15 | 16 | type +'child t [@@deriving sexp, of_jsonaf] 17 | 18 | val children : 'child t -> 'child list 19 | val after : _ t -> Page_id.t option 20 | val map : 'a t -> f:('a -> 'b) -> 'b t 21 | -------------------------------------------------------------------------------- /reddit_api_kernel/mod_action.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Id = struct 4 | type t = Uuid.Unstable.t [@@deriving compare, hash, sexp] 5 | 6 | let of_uuid = Fn.id 7 | let to_uuid = Fn.id 8 | 9 | let of_json_string string = 10 | match String.lsplit2_exn string ~on:'_' with 11 | | "ModAction", uuid -> Uuid.of_string uuid 12 | | _ -> raise_s [%message "Unexpected ModAction string" (string : string)] 13 | ;; 14 | end 15 | 16 | include Json_object.Utils 17 | 18 | include Json_object.Make_kinded_simple (struct 19 | let kind = "modaction" 20 | end) 21 | 22 | let id = required_field "id" (string >> Id.of_json_string) 23 | let action = required_field "action" string 24 | let details = required_field "details" string 25 | let created = required_field "created_utc" time_sec_since_epoch 26 | 27 | let target_author = 28 | required_field "target_author" (string >> Username.of_string_or_deleted) 29 | ;; 30 | 31 | let target_title = required_field "target_title" string 32 | let target_fullname = required_field "target_fullname" (string >> Thing.Fullname.of_string) 33 | let target_permalink = required_field "target_permalink" uri 34 | let subreddit_name = required_field "subreddit" subreddit_name 35 | let moderator = required_field "mod" (string >> Username.of_string_or_deleted) 36 | -------------------------------------------------------------------------------- /reddit_api_kernel/mod_action.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Id : sig 4 | type t [@@deriving compare, hash, sexp] 5 | 6 | val to_uuid : t -> Uuid.t 7 | val of_uuid : Uuid.t -> t 8 | end 9 | 10 | type t 11 | 12 | include Json_object.S_with_fields with type t := t 13 | include Json_object.S_with_kind with type t := t 14 | 15 | val id : t -> Id.t 16 | val action : t -> string 17 | val details : t -> string 18 | val created : t -> Time_ns.t 19 | val target_author : t -> Username.t option 20 | val target_title : t -> string 21 | val target_fullname : t -> Thing.Fullname.t 22 | val target_permalink : t -> Uri.t 23 | val subreddit_name : t -> Subreddit_name.t 24 | val moderator : t -> Username.t option 25 | -------------------------------------------------------------------------------- /reddit_api_kernel/moderator_report.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Jsonaf.Export 3 | 4 | type t = 5 | { moderator : Username.t option 6 | ; report : string 7 | } 8 | 9 | let t_of_jsonaf json = 10 | let report, moderator_string = [%of_jsonaf: string * string] json in 11 | { report; moderator = Username.of_string_or_deleted moderator_string } 12 | ;; 13 | -------------------------------------------------------------------------------- /reddit_api_kernel/moderator_report.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t = 4 | { moderator : Username.t option 5 | ; report : string 6 | } 7 | 8 | val t_of_jsonaf : Jsonaf.t -> t 9 | -------------------------------------------------------------------------------- /reddit_api_kernel/modmail.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Conversation = struct 4 | include Json_object.Utils 5 | 6 | module Id = struct 7 | include Id36 8 | 9 | include Identifiable.Make (struct 10 | include Id36 11 | 12 | let module_name = "Modmail.Conversation.Id" 13 | end) 14 | end 15 | 16 | let id = 17 | required_field "conversation" (fun json -> 18 | Jsonaf.member_exn "id" json |> string |> Id.of_string) 19 | ;; 20 | end 21 | -------------------------------------------------------------------------------- /reddit_api_kernel/modmail.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Conversation : sig 4 | type t [@@deriving sexp_of] 5 | 6 | module Id : Id36.S 7 | include Json_object.S_with_fields with type t := t 8 | 9 | val id : t -> Id.t 10 | end 11 | -------------------------------------------------------------------------------- /reddit_api_kernel/rate_limiter_state_machine.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module When_to_send : sig 4 | type t = 5 | | Now 6 | | Check_after_receiving_response 7 | | After of Time_ns.t 8 | end 9 | 10 | type t [@@deriving sexp_of] 11 | 12 | (** {1 Constructors} *) 13 | 14 | val by_headers : t 15 | val with_minimum_delay : delay:Time_ns.Span.t -> t 16 | val combine : t list -> t 17 | 18 | (** {1 Events} *) 19 | 20 | (** [sent_request_unchecked] should called immediately after sending a request. 21 | It is the caller's responsibility to first call {!wait_until}. *) 22 | val sent_request_unchecked : t -> now:Time_ns.t -> t 23 | 24 | val received_response : t -> Cohttp.Response.t -> t 25 | 26 | (** {1 Accessors} *) 27 | 28 | val wait_until : t -> When_to_send.t 29 | -------------------------------------------------------------------------------- /reddit_api_kernel/reddit_api_kernel.ml: -------------------------------------------------------------------------------- 1 | module Comment_response = Comment_response 2 | module Endpoint = Endpoint 3 | module Id36 = Id36 4 | module Inbox_item = Inbox_item 5 | module Json_object = Json_object 6 | module Karma_list = Karma_list 7 | module Listing = Listing 8 | module Mod_action = Mod_action 9 | module Moderator_report = Moderator_report 10 | module Modmail = Modmail 11 | module Rate_limiter_state_machine = Rate_limiter_state_machine 12 | module Relationship = Relationship 13 | module Stylesheet = Stylesheet 14 | module Submit_text = Submit_text 15 | module Subreddit_name = Subreddit_name 16 | module Subreddit_rules = Subreddit_rules 17 | module Subreddit_settings = Subreddit_settings 18 | module Subreddit_traffic = Subreddit_traffic 19 | module Thing = Thing 20 | module Thing_kind = Thing_kind 21 | module Uri_with_string_sexp = Uri_with_string_sexp 22 | module User_list = User_list 23 | module Username = Username 24 | module Wiki_page = Wiki_page 25 | -------------------------------------------------------------------------------- /reddit_api_kernel/relationship.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Jsonaf.Export 3 | include Relationship_intf 4 | 5 | module Common = struct 6 | module Id = String 7 | include Json_object.Utils 8 | 9 | let relationship_id = required_field "rel_id" string 10 | let username = required_field "name" username 11 | let user_id = required_field "id" (string >> Thing.User.Id.of_string) 12 | let date = required_field "date" time_sec_since_epoch 13 | end 14 | 15 | module Contributor = Common 16 | 17 | module Mute = struct 18 | include Common 19 | 20 | module Id = struct 21 | include Id 22 | 23 | let to_uuid t = String.chop_prefix_exn t ~prefix:"Mute_" |> Uuid.of_string 24 | let of_uuid uuid = sprintf !"Mute_%{Uuid}" uuid 25 | end 26 | end 27 | 28 | module Ban = struct 29 | include Common 30 | 31 | let note = required_field "note" string 32 | let days_left = optional_field "days_left" int 33 | end 34 | 35 | module Moderator = struct 36 | include Common 37 | 38 | let permissions = required_field "mod_permissions" [%of_jsonaf: string list] 39 | let flair_text = optional_field "author_flair_text" string 40 | let flair_css_class = optional_field "author_css_class" string 41 | end 42 | -------------------------------------------------------------------------------- /reddit_api_kernel/relationship.mli: -------------------------------------------------------------------------------- 1 | include Relationship_intf.Relationship (** @inline *) 2 | -------------------------------------------------------------------------------- /reddit_api_kernel/relationship_intf.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module type Id = sig 4 | type t [@@deriving sexp] 5 | 6 | include Stringable with type t := t 7 | end 8 | 9 | module type S = sig 10 | module Id : Id 11 | 12 | type t [@@deriving sexp] 13 | 14 | include Json_object.S_with_fields with type t := t 15 | 16 | val relationship_id : t -> Id.t 17 | val username : t -> Username.t 18 | val user_id : t -> Thing.User.Id.t 19 | val date : t -> Time_ns.t 20 | end 21 | 22 | module type Relationship = sig 23 | module type Id = Id 24 | module type S = S 25 | 26 | module Contributor : S 27 | 28 | module Mute : sig 29 | module Id : sig 30 | include Id 31 | 32 | val to_uuid : t -> Uuid.t 33 | val of_uuid : Uuid.t -> t 34 | end 35 | 36 | include S with module Id := Id 37 | end 38 | 39 | module Ban : sig 40 | include S 41 | 42 | val note : t -> string 43 | val days_left : t -> int option 44 | end 45 | 46 | module Moderator : sig 47 | include S 48 | 49 | val permissions : t -> string list 50 | val flair_text : t -> string option 51 | val flair_css_class : t -> string option 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /reddit_api_kernel/stylesheet.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Jsonaf.Export 3 | 4 | module Image = struct 5 | include Json_object.Utils 6 | 7 | let t_of_jsonaf json = 8 | match Jsonaf.assoc_list json with 9 | | Some alist -> Map.of_alist_exn (module String) alist 10 | | None -> raise_s [%message "Unexpected stylesheet image json" (json : Jsonaf.t)] 11 | ;; 12 | 13 | let url = required_field "url" uri 14 | let link = required_field "link" string 15 | let name = required_field "name" string 16 | end 17 | 18 | include Json_object.Utils 19 | 20 | include Json_object.Make_kinded_simple (struct 21 | let kind = "stylesheet" 22 | end) 23 | 24 | let images = required_field "images" [%of_jsonaf: Image.t list] 25 | let subreddit_id = required_field "subreddit_id" (string >> Thing.Subreddit.Id.of_string) 26 | let stylesheet_text = required_field "stylesheet" string 27 | -------------------------------------------------------------------------------- /reddit_api_kernel/stylesheet.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Image : sig 4 | type t 5 | 6 | val url : t -> Uri.t 7 | val link : t -> string 8 | val name : t -> string 9 | end 10 | 11 | type t 12 | 13 | include Json_object.S_with_fields with type t := t 14 | include Json_object.S_with_kind with type t := t 15 | 16 | val images : t -> Image.t list 17 | val subreddit_id : t -> Thing.Subreddit.Id.t 18 | val stylesheet_text : t -> string 19 | -------------------------------------------------------------------------------- /reddit_api_kernel/submit_text.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Json_object.Utils 3 | 4 | let submit_text markup = 5 | let field = 6 | match markup with 7 | | `markdown -> "submit_text" 8 | | `HTML -> "submit_text_html" 9 | in 10 | required_field field string 11 | ;; 12 | -------------------------------------------------------------------------------- /reddit_api_kernel/submit_text.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t [@@deriving sexp] 4 | 5 | include Json_object.S with type t := t 6 | 7 | val submit_text : [ `markdown | `HTML ] -> t -> string 8 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_name.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t = string 4 | 5 | include Identifiable.Make (struct 6 | include String.Caseless 7 | 8 | let module_name = "Subreddit_name" 9 | let to_string = Fn.id 10 | 11 | let of_string string = 12 | let try_prefix = List.find_map ~f:(fun prefix -> String.chop_prefix string ~prefix) in 13 | match try_prefix [ "u/"; "/u/" ] with 14 | | Some username -> "u_" ^ username 15 | | None -> try_prefix [ "r/"; "/r/" ] |> Option.value ~default:string 16 | ;; 17 | end) 18 | 19 | let user_subreddit username = of_string ("u_" ^ Username.to_string username) 20 | let all = "all" 21 | let combine = String.concat ~sep:"+" 22 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_name.mli: -------------------------------------------------------------------------------- 1 | (** [Subreddit_name] is a string identifier module that does some normalization: 2 | 3 | - Hashes and comparisons are caseless. 4 | - "r/" and "/r/" prefixes are dropped. 5 | - "u/" and "/u/" prefixes cause the string to be converted to the user's subreddit. 6 | *) 7 | 8 | open! Core 9 | 10 | type t 11 | 12 | (** [all] is /r/all, a special subreddit that includes items from most subreddits. 13 | 14 | Exceptions: 15 | 16 | {ol 17 | {- Some subreddits are excluded by Reddit administrators.} 18 | {- Some subreddits opt out of inclusion.} 19 | {- Users can filter individual subreddits out of their view of /r/all.}} 20 | *) 21 | val all : t 22 | 23 | (** [combine l] is a subreddit name representing the combination of the 24 | subreddits named in [l]. In general, when [combine l] is used as an API 25 | parameter the response contains items from all the subreddits in [l]. 26 | *) 27 | val combine : t list -> t 28 | 29 | (** [user_subreddit user] is the name of the special subreddit associated with 30 | the user's profile. Only the user can post to this subreddit, and on the 31 | desktop site links in this subreddit are associated with the user rather 32 | than with any visible subreddit. *) 33 | val user_subreddit : Username.t -> t 34 | 35 | include Identifiable.S with type t := t 36 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_rules.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Jsonaf.Export 3 | include Json_object.Utils 4 | 5 | module Rule = struct 6 | include Json_object.Utils 7 | 8 | module Kind = struct 9 | type t = 10 | | Link 11 | | Comment 12 | | All 13 | [@@deriving sexp] 14 | 15 | let of_string string = 16 | match string with 17 | | "link" -> Link 18 | | "comment" -> Comment 19 | | "all" -> All 20 | | _ -> 21 | raise_s 22 | [%message "Unrecognized [Subreddit_rules.Rule.Kind.t] string" (string : string)] 23 | ;; 24 | end 25 | 26 | let kind = required_field "kind" (string >> Kind.of_string) 27 | 28 | let description t markup = 29 | let field = 30 | match markup with 31 | | `markdown -> "description" 32 | | `HTML -> "description_html" 33 | in 34 | required_field field string t 35 | ;; 36 | 37 | let short_name = required_field "short_name" string 38 | let report_reason = required_field "violation_reason" string 39 | let creation_time = required_field "created_utc" time_sec_since_epoch 40 | let priority = required_field "priority" int 41 | end 42 | 43 | let subreddit_rules = required_field "rules" [%of_jsonaf: Rule.t list] 44 | let site_rules = required_field "site_rules" Fn.id 45 | let site_rules_flow = required_field "site_rules_flow" Fn.id 46 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_rules.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t [@@deriving sexp] 4 | 5 | include Json_object.S_with_fields with type t := t 6 | 7 | module Rule : sig 8 | type t [@@deriving sexp] 9 | 10 | include Json_object.S_with_fields with type t := t 11 | 12 | module Kind : sig 13 | type t = 14 | | Link 15 | | Comment 16 | | All 17 | [@@deriving sexp] 18 | end 19 | 20 | val kind : t -> Kind.t 21 | val description : t -> [ `markdown | `HTML ] -> string 22 | val short_name : t -> string 23 | val report_reason : t -> string 24 | val creation_time : t -> Time_ns.t 25 | val priority : t -> int 26 | end 27 | 28 | val subreddit_rules : t -> Rule.t list 29 | val site_rules : t -> Jsonaf.t 30 | val site_rules_flow : t -> Jsonaf.t 31 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_settings.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Json_object.Utils 3 | 4 | include Json_object.Make_kinded_simple (struct 5 | let kind = "subreddit_settings" 6 | end) 7 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_settings.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t [@@deriving sexp] 4 | 5 | include Json_object.S_with_fields with type t := t 6 | include Json_object.S_with_kind with type t := t 7 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_traffic.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Jsonaf.Export 3 | include Json_object.Utils 4 | 5 | let bail_on_json json ~module_name = 6 | let message = sprintf "Unexpected [Subreddit_traffic.%s.t] JSON" module_name in 7 | raise_s [%message message (json : Jsonaf.t)] 8 | ;; 9 | 10 | let get_time_and_rest json ~module_name = 11 | match Jsonaf.list_exn json with 12 | | time_json :: rest -> 13 | let time = time_sec_since_epoch time_json in 14 | let rest = List.map rest ~f:int in 15 | time, rest 16 | | _ -> bail_on_json json ~module_name 17 | ;; 18 | 19 | module By_date = struct 20 | type t = 21 | { date : Date.t 22 | ; uniques : int 23 | ; pageviews : int 24 | ; subscriptions : int 25 | } 26 | [@@deriving sexp] 27 | 28 | let t_of_jsonaf json = 29 | let module_name = "By_date" in 30 | match get_time_and_rest json ~module_name with 31 | | time, [ uniques; pageviews; subscriptions ] -> 32 | let date = Time_ns.to_date time ~zone:Time_float.Zone.utc in 33 | { date; uniques; pageviews; subscriptions } 34 | | _ -> bail_on_json json ~module_name 35 | ;; 36 | end 37 | 38 | module By_month = struct 39 | type t = 40 | { year : int 41 | ; month : Month.t 42 | ; uniques : int 43 | ; pageviews : int 44 | } 45 | [@@deriving sexp] 46 | 47 | let t_of_jsonaf json = 48 | let module_name = "By_month" in 49 | match get_time_and_rest json ~module_name with 50 | | time, [ uniques; pageviews ] -> 51 | let date = Time_ns.to_date time ~zone:Time_float.Zone.utc in 52 | let month = Date.month date in 53 | let year = Date.year date in 54 | { year; month; uniques; pageviews } 55 | | _ -> bail_on_json json ~module_name 56 | ;; 57 | end 58 | 59 | module By_hour = struct 60 | type t = 61 | { hour : Time_ns.Alternate_sexp.t 62 | ; uniques : int 63 | ; pageviews : int 64 | } 65 | [@@deriving sexp] 66 | 67 | let t_of_jsonaf json = 68 | let module_name = "By_hour" in 69 | match get_time_and_rest json ~module_name with 70 | | hour, [ uniques; pageviews ] -> { hour; uniques; pageviews } 71 | | _ -> bail_on_json json ~module_name 72 | ;; 73 | end 74 | 75 | let by_date = required_field "day" [%of_jsonaf: By_date.t list] 76 | let by_month = required_field "month" [%of_jsonaf: By_month.t list] 77 | let by_hour = required_field "hour" [%of_jsonaf: By_hour.t list] 78 | -------------------------------------------------------------------------------- /reddit_api_kernel/subreddit_traffic.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t [@@deriving sexp] 4 | 5 | include Json_object.S_with_fields with type t := t 6 | 7 | module By_date : sig 8 | type t = 9 | { date : Date.t 10 | ; uniques : int 11 | ; pageviews : int 12 | ; subscriptions : int 13 | } 14 | [@@deriving sexp] 15 | end 16 | 17 | module By_month : sig 18 | type t = 19 | { year : int 20 | ; month : Month.t 21 | ; uniques : int 22 | ; pageviews : int 23 | } 24 | [@@deriving sexp] 25 | end 26 | 27 | module By_hour : sig 28 | type t = 29 | { hour : Time_ns.t 30 | ; uniques : int 31 | ; pageviews : int 32 | } 33 | [@@deriving sexp] 34 | end 35 | 36 | val by_date : t -> By_date.t list 37 | val by_month : t -> By_month.t list 38 | val by_hour : t -> By_hour.t list 39 | -------------------------------------------------------------------------------- /reddit_api_kernel/thing.mli: -------------------------------------------------------------------------------- 1 | include Thing_intf.Thing (** @inline *) 2 | -------------------------------------------------------------------------------- /reddit_api_kernel/thing_kind.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t = 4 | | Comment 5 | | User 6 | | Link 7 | | Message 8 | | Subreddit 9 | | Award 10 | | More_comments 11 | | Modmail_conversation 12 | [@@deriving sexp, equal] 13 | 14 | let of_string s = 15 | match s with 16 | | "t1" -> Comment 17 | | "t2" -> User 18 | | "t3" -> Link 19 | | "t4" -> Message 20 | | "t5" -> Subreddit 21 | | "t6" -> Award 22 | | "more" -> More_comments 23 | | "modmail" -> Modmail_conversation 24 | | _ -> raise_s [%message "Unknown thing kind" s] 25 | ;; 26 | 27 | let to_string t = 28 | match t with 29 | | Comment -> "t1" 30 | | User -> "t2" 31 | | Link -> "t3" 32 | | Message -> "t4" 33 | | Subreddit -> "t5" 34 | | Award -> "t6" 35 | | More_comments -> "more" 36 | | Modmail_conversation -> "modmail" 37 | ;; 38 | 39 | let to_string_long t = 40 | match t with 41 | | Comment -> "Comment" 42 | | User -> "User" 43 | | Link -> "Link" 44 | | Message -> "Message" 45 | | Subreddit -> "Subreddit" 46 | | Award -> "Award" 47 | | More_comments -> "more" 48 | | Modmail_conversation -> "modmail" 49 | ;; 50 | 51 | let of_polymorphic_tag = function 52 | | `Comment _ -> Comment 53 | | `User _ -> User 54 | | `Link _ -> Link 55 | | `Message _ -> Message 56 | | `Subreddit _ -> Subreddit 57 | | `Award _ -> Award 58 | | `More_comments _ -> More_comments 59 | | `Modmail_conversation _ -> Modmail_conversation 60 | ;; 61 | 62 | let of_polymorphic_tag_with_uniform_data = function 63 | | `Comment data -> Comment, data 64 | | `User data -> User, data 65 | | `Link data -> Link, data 66 | | `Message data -> Message, data 67 | | `Subreddit data -> Subreddit, data 68 | | `Award data -> Award, data 69 | | `More_comments data -> More_comments, data 70 | | `Modmail_conversation data -> Modmail_conversation, data 71 | ;; 72 | 73 | let to_polymorphic_tag 74 | t 75 | ~data 76 | ~award 77 | ~comment 78 | ~link 79 | ~message 80 | ~modmail_conversation 81 | ~more_comments 82 | ~subreddit 83 | ~user 84 | = 85 | match t with 86 | | Comment -> `Comment (comment data) 87 | | User -> `User (user data) 88 | | Link -> `Link (link data) 89 | | Message -> `Message (message data) 90 | | Subreddit -> `Subreddit (subreddit data) 91 | | Award -> `Award (award data) 92 | | More_comments -> `More_comments (more_comments data) 93 | | Modmail_conversation -> `Modmail_conversation (modmail_conversation data) 94 | ;; 95 | 96 | let to_polymorphic_tag_uniform t ~data = 97 | to_polymorphic_tag 98 | t 99 | ~data 100 | ~award:Fn.id 101 | ~comment:Fn.id 102 | ~link:Fn.id 103 | ~message:Fn.id 104 | ~modmail_conversation:Fn.id 105 | ~more_comments:Fn.id 106 | ~subreddit:Fn.id 107 | ~user:Fn.id 108 | ;; 109 | -------------------------------------------------------------------------------- /reddit_api_kernel/thing_kind.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t = 4 | | Comment 5 | | User 6 | | Link 7 | | Message 8 | | Subreddit 9 | | Award 10 | | More_comments 11 | | Modmail_conversation 12 | [@@deriving sexp, equal] 13 | 14 | include Stringable.S with type t := t 15 | 16 | val to_string_long : t -> string 17 | 18 | val of_polymorphic_tag 19 | : [< `Award of _ 20 | | `Comment of _ 21 | | `Link of _ 22 | | `Message of _ 23 | | `Modmail_conversation of _ 24 | | `More_comments of _ 25 | | `Subreddit of _ 26 | | `User of _ 27 | ] 28 | -> t 29 | 30 | val of_polymorphic_tag_with_uniform_data 31 | : [< `Award of 'data 32 | | `Comment of 'data 33 | | `Link of 'data 34 | | `Message of 'data 35 | | `Modmail_conversation of 'data 36 | | `More_comments of 'data 37 | | `Subreddit of 'data 38 | | `User of 'data 39 | ] 40 | -> t * 'data 41 | 42 | val to_polymorphic_tag 43 | : t 44 | -> data:'data 45 | -> award:('data -> 'award) 46 | -> comment:('data -> 'comment) 47 | -> link:('data -> 'link) 48 | -> message:('data -> 'message) 49 | -> modmail_conversation:('data -> 'modmail_conversation) 50 | -> more_comments:('data -> 'more_comments) 51 | -> subreddit:('data -> 'subreddit) 52 | -> user:('data -> 'user) 53 | -> [> `Award of 'award 54 | | `Comment of 'comment 55 | | `Link of 'link 56 | | `Message of 'message 57 | | `Modmail_conversation of 'modmail_conversation 58 | | `More_comments of 'more_comments 59 | | `Subreddit of 'subreddit 60 | | `User of 'user 61 | ] 62 | 63 | val to_polymorphic_tag_uniform 64 | : t 65 | -> data:'data 66 | -> [> `Award of 'data 67 | | `Comment of 'data 68 | | `Link of 'data 69 | | `Message of 'data 70 | | `Modmail_conversation of 'data 71 | | `More_comments of 'data 72 | | `Subreddit of 'data 73 | | `User of 'data 74 | ] 75 | -------------------------------------------------------------------------------- /reddit_api_kernel/uri_with_string_sexp.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Uri_sexp 3 | 4 | let sexp_of_t t : Sexp.t = Atom (Uri.to_string t) 5 | let t_of_sexp sexp = Uri.of_string ([%of_sexp: string] sexp) 6 | -------------------------------------------------------------------------------- /reddit_api_kernel/uri_with_string_sexp.mli: -------------------------------------------------------------------------------- 1 | (** [Uri_with_string_sexp] is {!module:Uri_sexp} with a different sexp 2 | serialization: 3 | 4 | [sexp_of_t t = Atom (Uri.to_string t)] 5 | *) 6 | open! Core 7 | 8 | include module type of struct 9 | include Uri_sexp 10 | end 11 | -------------------------------------------------------------------------------- /reddit_api_kernel/user_list.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Item = struct 4 | module Id = String 5 | include Json_object.Utils 6 | 7 | let username = required_field "name" username 8 | let user_id = required_field "id" (string >> Thing.User.Id.of_string) 9 | let relationship_id = required_field "rel_id" string 10 | let since = required_field "date" time_sec_since_epoch 11 | end 12 | 13 | type t = Item.t list [@@deriving sexp] 14 | -------------------------------------------------------------------------------- /reddit_api_kernel/user_list.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Item : sig 4 | module Id : Identifiable 5 | 6 | type t [@@deriving sexp] 7 | 8 | include Json_object.S_with_fields with type t := t 9 | 10 | val username : t -> Username.t 11 | val user_id : t -> Thing.User.Id.t 12 | val relationship_id : t -> Id.t 13 | val since : t -> Time_ns.t 14 | end 15 | 16 | type t = Item.t list [@@deriving sexp] 17 | -------------------------------------------------------------------------------- /reddit_api_kernel/username.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | type t = string 4 | 5 | include Identifiable.Make (struct 6 | include String.Caseless 7 | 8 | let module_name = "Username" 9 | let to_string = Fn.id 10 | 11 | let of_string string = 12 | List.find_map [ "u/"; "/u/" ] ~f:(fun prefix -> String.chop_prefix string ~prefix) 13 | |> Option.value ~default:string 14 | ;; 15 | end) 16 | 17 | let of_string_or_deleted string = 18 | match string with 19 | | "[deleted]" -> None 20 | | _ -> Some (of_string string) 21 | ;; 22 | -------------------------------------------------------------------------------- /reddit_api_kernel/username.mli: -------------------------------------------------------------------------------- 1 | (** [Username] is a string identifier module that does some normalization: 2 | 3 | - Hashes and comparisons are caseless. 4 | - "u/" and "/u/" prefixes are dropped. 5 | *) 6 | 7 | open! Core 8 | include Identifiable.S 9 | 10 | val of_string_or_deleted : string -> t option 11 | -------------------------------------------------------------------------------- /reddit_api_kernel/wiki_page.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open Jsonaf.Export 3 | 4 | module Id = struct 5 | type t = 6 | { subreddit : Subreddit_name.t option 7 | ; page : string 8 | } 9 | [@@deriving sexp] 10 | end 11 | 12 | module Revision = struct 13 | include Json_object.Utils 14 | 15 | module Id = struct 16 | include Uuid 17 | include Uuid.Unstable 18 | 19 | let of_uuid = Fn.id 20 | let to_uuid = Fn.id 21 | end 22 | 23 | let page_name = required_field "page" string 24 | let id = required_field "id" (string >> Id.of_string) 25 | let reason = optional_field "reason" string 26 | let timestamp = required_field "timestamp" time_sec_since_epoch 27 | let hidden = required_field "revision_hidden" bool 28 | let author = optional_field "author" [%of_jsonaf: Thing.User.t] 29 | end 30 | 31 | module Permissions = struct 32 | include Json_object.Utils 33 | 34 | include Json_object.Make_kinded_simple (struct 35 | let kind = "wikipagesettings" 36 | end) 37 | 38 | module Level = struct 39 | type t = 40 | | Use_subreddit_wiki_permissions 41 | | Only_approved_contributors_for_this_page 42 | | Only_moderators 43 | [@@deriving sexp] 44 | 45 | let of_int_exn level = 46 | match level with 47 | | 0 -> Use_subreddit_wiki_permissions 48 | | 1 -> Only_approved_contributors_for_this_page 49 | | 2 -> Only_moderators 50 | | _ -> raise_s [%message "Unrecognized wiki page permission level" (level : int)] 51 | ;; 52 | 53 | let to_int t = 54 | match t with 55 | | Use_subreddit_wiki_permissions -> 0 56 | | Only_approved_contributors_for_this_page -> 1 57 | | Only_moderators -> 2 58 | ;; 59 | end 60 | 61 | let level = required_field "permlevel" (int >> Level.of_int_exn) 62 | let contributors = required_field "editors" [%of_jsonaf: Thing.User.t list] 63 | let listed = required_field "listed" bool 64 | end 65 | 66 | include Json_object.Utils 67 | 68 | include Json_object.Make_kinded_simple (struct 69 | let kind = "wikipage" 70 | end) 71 | 72 | let may_revise = required_field "may_revise" bool 73 | let revision_id = required_field "revision_id" (string >> Uuid.of_string) 74 | let revision_by = required_field "revision_by" [%of_jsonaf: Thing.User.t] 75 | 76 | let content t markup = 77 | let field = 78 | match markup with 79 | | `markdown -> "content_md" 80 | | `HTML -> "content_html" 81 | in 82 | required_field field string t 83 | ;; 84 | 85 | let revision_time = required_field "revision_date" time_sec_since_epoch 86 | let revision_reason = optional_field "reason" string 87 | 88 | module Edit_conflict = struct 89 | include Json_object.Utils 90 | 91 | let diff = required_field "diffcontent" string 92 | let message = required_field "message" string 93 | let new_content = required_field "newcontent" string 94 | let new_revision = required_field "newrevision" (string >> Uuid.of_string) 95 | let reason = optional_field "reason" string 96 | end 97 | -------------------------------------------------------------------------------- /reddit_api_kernel/wiki_page.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Id : sig 4 | type t = 5 | { subreddit : Subreddit_name.t option 6 | ; page : string 7 | } 8 | [@@deriving sexp] 9 | end 10 | 11 | module Revision : sig 12 | type t [@@deriving sexp] 13 | 14 | include Json_object.S_with_fields with type t := t 15 | 16 | module Id : sig 17 | type t [@@deriving sexp] 18 | 19 | include Stringable with type t := t 20 | 21 | val to_uuid : t -> Uuid.t 22 | val of_uuid : Uuid.t -> t 23 | end 24 | 25 | val page_name : t -> string 26 | val id : t -> Id.t 27 | val reason : t -> string option 28 | val timestamp : t -> Time_ns.t 29 | val hidden : t -> bool 30 | val author : t -> Thing.User.t option 31 | end 32 | 33 | module Edit_conflict : sig 34 | type t [@@deriving sexp] 35 | 36 | include Json_object.S_with_fields with type t := t 37 | 38 | val diff : t -> string 39 | val message : t -> string 40 | val new_content : t -> string 41 | val new_revision : t -> Revision.Id.t 42 | val reason : t -> string option 43 | end 44 | 45 | module Permissions : sig 46 | type t [@@deriving sexp] 47 | 48 | include Json_object.S_with_fields with type t := t 49 | include Json_object.S_with_kind with type t := t 50 | 51 | module Level : sig 52 | type t = 53 | | Use_subreddit_wiki_permissions 54 | | Only_approved_contributors_for_this_page 55 | | Only_moderators 56 | [@@deriving sexp] 57 | 58 | val of_int_exn : int -> t 59 | val to_int : t -> int 60 | end 61 | 62 | val level : t -> Level.t 63 | val contributors : t -> Thing.User.t list 64 | val listed : t -> bool 65 | end 66 | 67 | type t [@@deriving sexp] 68 | 69 | include Json_object.S_with_fields with type t := t 70 | include Json_object.S_with_kind with type t := t 71 | 72 | val may_revise : t -> bool 73 | val revision_id : t -> Revision.Id.t 74 | val revision_by : t -> Thing.User.t 75 | val content : t -> [ `markdown | `HTML ] -> string 76 | val revision_time : t -> Time_ns.t 77 | val revision_reason : t -> string option 78 | -------------------------------------------------------------------------------- /test/cassettes/add_wiki_editor.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:02:22 GMT")(server snooserv)(set-cookie"edgebucket=RqQSitfZpIbrRIRU1p; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/wiki/alloweditor/add)(headers((authorization"bearer ")))(params((raw_json(1))(page(index))(username(L72_Elite_Kraken))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:02:22 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 25-Oct-2023 15:02:22 GMT; secure")(set-cookie"session_tracker=rmgjdbnkmjjaibalij.0.1603638142885.Z0FBQUFBQmZsWk4tLW5Qb29CUVg4aGRGa1lKaW9mSEQxT1Z4ZFFQNDZpZXFYcmM0RWoxLVRFZzFEcGpmV05MendwY1RqSFBDeExmVzFDaGlqNkpSZGxfYUZzMm9sQWU1bGhXVUJXX2k1YnlhZ1NCQkxCbGd4SzFGMkNXcE8xRmJVWHROeFl4b3BqRTI; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 25-Oct-2020 17:02:22 GMT; secure")(set-cookie"edgebucket=gUxVOc2AySfoUbd8tX; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 458)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/ban__wrong_subreddit.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 117))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 117)(content-type"application/json; charset=UTF-8")(date"Sun, 10 Jan 2021 00:55:31 GMT")(server snooserv)(set-cookie"edgebucket=HagFKSaocXup9966d9; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/thirdrealm/api/friend)(headers((authorization"bearer ")))(params((raw_json(1))(duration())(type(banned))(duration())(type(banned))(api_type(json))(name(spez))))))(response(((encoding(Fixed 38))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 38)(content-type"application/json; charset=UTF-8")(date"Sun, 10 Jan 2021 00:55:32 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 10-Jan-2024 00:55:32 GMT; secure")(set-cookie"session_tracker=pamdirikqhamckmbrp.0.1610240132005.Z0FBQUFBQmYtbENFTWQza2pPQXR3bnRyOHRKcjFtMUtjbXp5Z2w1Rmo2WmJVQndIQmxmRTN0Q0g5YkluRElNUjVXYWwwTXhvT1JmdzE1NEp0eFEzaWlhQ3Z2YUNISVZqZG5pcXJFTm5aaE1UN3ZPZlQ3d3pHcnlDcHFDSS1GNW45YjJkRUtGUzVuU2M; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 10-Jan-2021 02:55:32 GMT; secure")(set-cookie"loid=00000000009qw1prk0.2.1610240132033.Z0FBQUFBQmYtbENFTk9YUHNiY0tqNEJDcncwckVWNTBIMVoxeGplWkt3TXZDaU9JYk14bU5HeVNjaHlsS0RVOXF3eEhKTXRQNEZyV091bUYtcDJBUmQ2MzVuMTZ4VnlIUkMtMkJFYWhpX0ZucWExdGh6elIzM1NUdDExMU5rWkdZZHlySkZ3a1k2SjA; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Tue, 10-Jan-2023 00:55:32 GMT; secure")(set-cookie"session_tracker=NOOmE3IXW7B1Bb3vgh.0.1610240132033.Z0FBQUFBQmYtbENFNHhtdXN2OU85MDFLME1JcEI4a25YUGwtekFMUGZNN3p6ZjBycGg3UXF4MFJweUtGX0hQZWgtRWR2MmNyV1J0NkJWRmZzUDM3WmlQS3dUUTQzSHhvYTRyOUw5SEx4ay1PQWFuZDBYTTdhR0s0QmJqaWpOTGxxcDRSMGVYRlBvSWQ; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 10-Jan-2021 02:55:32 GMT; secure")(set-cookie"edgebucket=4TDJWzDvZTLgwMNSCI; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 268)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status Forbidden)(flush false))"{\"message\": \"Forbidden\", \"error\": 403}"))) -------------------------------------------------------------------------------- /test/cassettes/banned.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 17 Sep 2020 12:50:35 GMT")(server snooserv)(set-cookie"edgebucket=oh1rJcwGrn63dTzzoe; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ThirdRealm/about/banned?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 375))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 375)(content-type"application/json; charset=UTF-8")(date"Thu, 17 Sep 2020 12:50:35 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=meioopcikfpibleorc.0.1600347035788.Z0FBQUFBQmZZMXViclVPczVXS1Q4M0FyV2l1dHJhSzM0UnBKTXRLSC15MkhLUUxqNEZJc0drdDJqbmR5SkU3OHgxdDhXLUZfSmFuSDRnREE5YmU3LVozMm5kMEVTU0VhN2hacGNfX1dHZGdpaGZ3aFdUMkRUVmR4NXJyYnZzeXBWN25oTVlmMnc2VEE; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 17-Sep-2020 14:50:35 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 17-Sep-2023 12:50:35 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=gB1NSnqLPP7IArh7Cv; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 565)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"Listing\", \"data\": {\"modhash\": null, \"dist\": null, \"children\": [{\"name\": \"ketralnis\", \"days_left\": 2, \"note\": \"blah blah blah\", \"date\": 1600347033.0, \"rel_id\": \"rb_26zyq4m\", \"id\": \"t2_nn0q\"}, {\"name\": \"spez\", \"days_left\": null, \"note\": \"blah blah blah: Capricious ban\", \"date\": 1600346972.0, \"rel_id\": \"rb_26zynhm\", \"id\": \"t2_1w72\"}], \"after\": null, \"before\": null}}"))) -------------------------------------------------------------------------------- /test/cassettes/block_author.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:33:54 GMT")(server snooserv)(set-cookie"edgebucket=rjeIjIeCWvMie4u4zJ; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/block)(headers((authorization"bearer ")))(params((raw_json(1))(id(t4_rdjz4y))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:33:54 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 03-Sep-2023 11:33:54 GMT; secure")(set-cookie"session_tracker=fnadfbgfaepppghipg.0.1599132834170.Z0FBQUFBQmZVTlNpWEFmX3BBVm9fYVNZQmRsZV9GYTYtZ3dldkRPQU5ieTVpamVaZWhOZWIzNHlvZjF3enhOdXV6ZXJYdDk3OXFkTE1VOXROSDk0bGJSWmpxMkVGY0FQWDZOcnRoa2FBVGRzRVJzSTJsRWhsTGMyTWxXX0lWcVRMMl9YRDNKT283ZmY; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 03-Sep-2020 13:33:54 GMT; secure")(set-cookie"edgebucket=I9L5ILObFFCu4jqopM; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 366)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/blocked.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 30 Aug 2020 12:04:28 GMT")(server snooserv)(set-cookie"edgebucket=lXYgUs7xkMZTsWAnj4; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/prefs/blocked?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 129))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 129)(content-type"application/json; charset=UTF-8")(date"Sun, 30 Aug 2020 12:04:29 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=mligfrlljmffiqejmp.0.1598789069287.Z0FBQUFBQmZTNVhOdDJnSlVWWHNZOG95MWZ4MnBXbUhDXzI0Q01fbFBORVZfd19kaFRSbUFYd0ZtbHNJcGswQ094RmJHUDRfNFBodVFvN3NoRG9yTmNyZHhubGc1YUFiaUYtV2RWS3RKV182djFJc0ZxWTBXMjhYX0p2aGFucXM2ckphSDVEVE83NGc; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 30-Aug-2020 14:04:29 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 30-Aug-2023 12:04:29 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=hva6CW1TtSBjRKdy1P; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 331)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"UserList\", \"data\": {\"children\": [{\"date\": 1598788910.0, \"rel_id\": \"r9_1vt16j\", \"name\": \"ketralnis\", \"id\": \"t2_nn0q\"}]}}"))) -------------------------------------------------------------------------------- /test/cassettes/collapse_message.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:38:41 GMT")(server snooserv)(set-cookie"edgebucket=NCScB1POaVpfuRYWyS; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/collapse_message)(headers((authorization"bearer ")))(params((raw_json(1))(id(t4_rdjz4y))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:38:41 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 03-Sep-2023 11:38:41 GMT; secure")(set-cookie"session_tracker=ffaccofhrgjkcnnnno.0.1599133121567.Z0FBQUFBQmZVTlhCZXMzajdNOTJlRWdJaFc3V2NpWjZLNnlnOVB1Sy1MUEF2dEpBdFBWQi0xYXZBZTgtMjdDaUdMSXV2NVJickZZRTBEeXI2WWFPME5hSEpaLS03ZGNKYWIxZlZ3QjJISjg3V2N1X3FDSU5CSXpYUkdpRlJ4LTZHOThSZW1jWTRDOHc; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 03-Sep-2020 13:38:41 GMT; secure")(set-cookie"edgebucket=v16fSmagQ4rlvo6tRG; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 79)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/compose_message.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:44:45 GMT")(server snooserv)(set-cookie"edgebucket=KAY3Dl0syO6fYskS8R; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/compose)(headers((authorization"bearer ")))(params((raw_json(1))(api_type(json))(to(BJO_test_user))(subject("This is a message"))(text("This is its body"))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:44:45 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 03-Sep-2023 11:44:45 GMT; secure")(set-cookie"session_tracker=bkeqepceaqmbeccoil.0.1599133485373.Z0FBQUFBQmZVTmN0RG1XTDdDVEctRVNYWE1tUWRONUJ5ZmQ3WDVUSFhYcDNhd1RPSUxSZldjTHdxaDhnaUQxVkZaamIwWmVHMUQtOU9hZWdFNmVrTzFOSTBWeGtaYnVRUTNRNk85clhNUFJCaHdILU1UaG1GNXJDNE9hRXRjWXAyQUZmVGo4Y2tVZ1o; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 03-Sep-2020 13:44:45 GMT; secure")(set-cookie"edgebucket=MRxubKJMYU0XMrD0OM; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 315)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/contributors.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sat, 19 Sep 2020 23:24:27 GMT")(server snooserv)(set-cookie"edgebucket=TjgCENvXAX0QKxZ8Q8; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ThirdRealm/about/contributors?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 284))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 284)(content-type"application/json; charset=UTF-8")(date"Sat, 19 Sep 2020 23:24:27 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=edljqrgdinoafnbfig.0.1600557867135.Z0FBQUFBQmZacE1yekFJZ3kwVDR5eFpOSmpLQi1HUFRyVk8wQWpjcFhoMVlESzVQR3dUeWpYaW5iMFVsOHAzVnRCMWZwSlROUmlwRTd3aWVkQzhWSkJENU1VUERrTVNTTVJZcEt6OXdvMlFxVWRsLTRuVVNoV2R5LWt4UkZOM1dpZjVWbnh2TGJ6ajY; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 20-Sep-2020 01:24:27 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Tue, 19-Sep-2023 23:24:27 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=RSBqbXic4JDhwaNMfW; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 333)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"Listing\", \"data\": {\"modhash\": null, \"dist\": null, \"children\": [{\"date\": 1508279509.0, \"rel_id\": \"rb_rktlv8\", \"name\": \"BJO_test_user\", \"id\": \"t2_xw1ym\"}, {\"date\": 1465092742.0, \"rel_id\": \"rb_g6xsft\", \"name\": \"BJO_test_mod\", \"id\": \"t2_xw27h\"}], \"after\": null, \"before\": null}}"))) -------------------------------------------------------------------------------- /test/cassettes/delete.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Mon, 31 Aug 2020 01:08:02 GMT")(server snooserv)(set-cookie"edgebucket=5f9obgvEbEMhfE7ReX; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/del)(headers((authorization"bearer ")))(params((raw_json(1))(id(t1_g3f4icy))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Mon, 31 Aug 2020 01:08:03 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Thu, 31-Aug-2023 01:08:02 GMT; secure")(set-cookie"session_tracker=pinpfoeakrgilkiioi.0.1598836082759.Z0FBQUFBQmZURTF6SFpCRktua1FrYThCdWhHTlNhclRGSUNtRFpIZ2xzRXFsTF94NklaRVN0Rk5UVXpBcEZNR3ZibGpmV3hEMElZQzBUTG1WT2hKMDE2azhkWU5iR1ZNVFRLenk3NzhQbGpoX2ZMMm9ZeFZzX3V4M0stM3FiRjlYbUxvTGlPQlYwOG0; Domain=reddit.com; Max-Age=7199; Path=/; expires=Mon, 31-Aug-2020 03:08:03 GMT; secure")(set-cookie"edgebucket=gfOgh04Cj00MuzQBW8; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 118)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/delete_subreddit_banner.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Mon, 21 Sep 2020 12:36:45 GMT")(server snooserv)(set-cookie"edgebucket=kpXuCbxtLPQkSSVAXr; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/delete_sr_banner)(headers((authorization"bearer ")))(params((raw_json(1))(api_type(json))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Mon, 21 Sep 2020 12:36:45 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Thu, 21-Sep-2023 12:36:45 GMT; secure")(set-cookie"session_tracker=iccemqhhrfdohmgfmr.0.1600691805708.Z0FBQUFBQmZhSjVka184WjZ4bDdJT29oeDRJVnlEUjJFYmpxRTNXdzlvVU81U2tIVXVtZ1hVRGk4XzFfMm5DYkVhc0hOUEFtMENoUlFURm9CVDYtcWNGb09UcUNSdmJsdEZ4Rjg0T3J1SGFBc3pldjgyd1VvX1c0em1rR2hTYW11WTJRVHhxa0tqNVA; Domain=reddit.com; Max-Age=7199; Path=/; expires=Mon, 21-Sep-2020 14:36:45 GMT; secure")(set-cookie"edgebucket=3GP6KDoZPKH7EJZ1EY; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 195)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/delete_subreddit_header.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Mon, 21 Sep 2020 12:39:34 GMT")(server snooserv)(set-cookie"edgebucket=GprUetzbSa2iJIg19W; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/delete_sr_header)(headers((authorization"bearer ")))(params((raw_json(1))(api_type(json))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Mon, 21 Sep 2020 12:39:34 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Thu, 21-Sep-2023 12:39:34 GMT; secure")(set-cookie"session_tracker=onoeajgdoicpdpgijr.0.1600691974512.Z0FBQUFBQmZhSjhHdXVzaHMyX2ktdWQzdV9uMWV0WFhZdWJlNEYxVWI2dnI1OEhaYUNsT0YxSlFLSEZvejMxLWJCOHY3NG1RdEdRQXpVekk0bTB2SVJxWUp3Y3YyeVlWcDVicGZ3YjFpeFZYVmRZVzNwX19vOTZGQk9MOHlVUnhvRGhuVmxKSFVJX1g; Domain=reddit.com; Max-Age=7199; Path=/; expires=Mon, 21-Sep-2020 14:39:34 GMT; secure")(set-cookie"edgebucket=rbgKkixOgq7idQODa3; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 597.0)(x-ratelimit-reset 26)(x-ratelimit-used 3)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/delete_subreddit_icon.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Mon, 21 Sep 2020 12:39:34 GMT")(server snooserv)(set-cookie"edgebucket=QQvzy2pYhy9m72fe7C; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/delete_sr_icon)(headers((authorization"bearer ")))(params((raw_json(1))(api_type(json))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Mon, 21 Sep 2020 12:39:35 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Thu, 21-Sep-2023 12:39:35 GMT; secure")(set-cookie"session_tracker=jlbpdhpakercgmnmhn.0.1600691975039.Z0FBQUFBQmZhSjhIM1dhdkxyRnRFVFdIcS1sSWtzZFRkRFV2NlVwRGlzb0psMERpRHctM1dSSktRMFEtMFhVdWxRemduMDdDT3pNcEJuMklYZGp6NDhWVGxIYnFYcXhoV2hJMDVBZGNJRk1GQVZVbl9Ud25OS3F4WGJYdXpRY0FFQWRqRTFYbVl5bFU; Domain=reddit.com; Max-Age=7199; Path=/; expires=Mon, 21-Sep-2020 14:39:35 GMT; secure")(set-cookie"edgebucket=cWPb8BmXhY2SUdfY1H; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 596.0)(x-ratelimit-reset 25)(x-ratelimit-used 4)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/delete_subreddit_image.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Tue, 22 Sep 2020 02:03:24 GMT")(server snooserv)(set-cookie"edgebucket=ndFla9F01W81K0U2rp; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/delete_sr_img)(headers((authorization"bearer ")))(params((raw_json(1))(api_type(json))(img_name(leviroth))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Tue, 22 Sep 2020 02:03:24 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Fri, 22-Sep-2023 02:03:24 GMT; secure")(set-cookie"session_tracker=qrjlinhkfngqpirnbr.0.1600740204584.Z0FBQUFBQmZhVnRzVnl4ZGg3c1Y1MUZ6cG4zS3dqWEJ2LXAwYURsWEhVSHZNMW1PWng1eUk2RE54aDVHY2x6N1VYcVA5VUZJTldIcU00QTVMX1BaZmdrXzJscFpJdDVsVzhHZ1EwYjFqM05PRlJOcHNscjc0UlR0Qm91bDhPZGdHMFJzcm8yU2Q0UDQ; Domain=reddit.com; Max-Age=7199; Path=/; expires=Tue, 22-Sep-2020 04:03:24 GMT; secure")(set-cookie"edgebucket=uew40BQkxxqg9yo6IV; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 396)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/friends.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Fri, 28 Aug 2020 23:19:23 GMT")(server snooserv)(set-cookie"edgebucket=oYSUqWDWMyydexNwlz; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/prefs/friends?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 174))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 174)(content-type"application/json; charset=UTF-8")(date"Fri, 28 Aug 2020 23:19:23 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=irqbgaomdqafgobrmc.0.1598656763941.Z0FBQUFBQmZTWkQ3VWppdlkyWThtaGJ6alV6ck03TUxDVlU0QVFkRVE1MHB4U3hERWdCSXcwQzZmY3JVZzI3dGVRTS1nZVdpY2sybHA3anJqdWRfS1N5bl9YZ3ZaYURubWZSbVgwbWdPWVZmZGU0QXUyNDhmbXhtX21JeXBHOXJKdnVQRVY4Y0I0VXU; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sat, 29-Aug-2020 01:19:23 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Mon, 28-Aug-2023 23:19:23 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=k6WNhmNChGu7vGW0mu; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 37)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"[{\"kind\": \"UserList\", \"data\": {\"children\": [{\"date\": 1598656681.0, \"rel_id\": \"r9_1voxr1\", \"name\": \"spez\", \"id\": \"t2_1w72\"}]}}, {\"kind\": \"UserList\", \"data\": {\"children\": []}}]"))) -------------------------------------------------------------------------------- /test/cassettes/ignore_reports.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Wed, 09 Sep 2020 11:25:37 GMT")(server snooserv)(set-cookie"edgebucket=pPXqd3D9tt9TSTpLq4; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/ignore_reports)(headers((authorization"bearer ")))(params((raw_json(1))(id(t3_ili4vc))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Wed, 09 Sep 2020 11:25:37 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sat, 09-Sep-2023 11:25:37 GMT; secure")(set-cookie"session_tracker=jmcgbegnkrlcojrhhc.0.1599650737560.Z0FBQUFBQmZXTHV4WlNGZk9iVmVCeU5Sd09VcW5ZOWIzWVBOa09iQzR1TEJ1YTRjeTNnTHp3cU9WTC1WeVM1d3phakNwMUdOeEUtSXAtdEFObnlmSnkybUszOGQxTGh2UVJvN3ZqYm5FNzdaYWZSWnZBdy1NSUROWDR3eVY4Mm96M01IS28yMzExbC0; Domain=reddit.com; Max-Age=7199; Path=/; expires=Wed, 09-Sep-2020 13:25:37 GMT; secure")(set-cookie"edgebucket=zGFEfhobunoly9tJZX; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 263)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/leavecontributor.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 10 Sep 2020 22:27:16 GMT")(server snooserv)(set-cookie"edgebucket=dhnjLZhbhKAu7kmgMk; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/leavecontributor)(headers((authorization"bearer ")))(params((raw_json(1))(id(t5_390u2))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Thu, 10 Sep 2020 22:27:16 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 10-Sep-2023 22:27:16 GMT; secure")(set-cookie"session_tracker=bnnjnfjfmedpilapkf.0.1599776836415.Z0FBQUFBQmZXcWhFNnBidXZLSEdZWVNxVEh2MDdjeklkaEhBX3hzTlhCMjJsQTl4TGtwVXVnNVUyZUxOZ2xSTlgzTDUySW03RjlkM2RVNE5qdnBvaDBuQjNDVFdWNENiS1hxTU1haG5lT0dabzd4a3F6dDViMWd3YWVDTWExZjhSLURvRFJ3NHlIeGU; Domain=reddit.com; Max-Age=7199; Path=/; expires=Fri, 11-Sep-2020 00:27:16 GMT; secure")(set-cookie"edgebucket=wYrxeIjiAZP92UqcFU; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 164)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/leavemoderator.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 10 Sep 2020 22:28:50 GMT")(server snooserv)(set-cookie"edgebucket=XXbEpRcrOmvrUlMGCw; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/leavemoderator)(headers((authorization"bearer ")))(params((raw_json(1))(id(t5_390u2))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Thu, 10 Sep 2020 22:28:50 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 10-Sep-2023 22:28:50 GMT; secure")(set-cookie"session_tracker=jqkkafiqjmdpojarge.0.1599776930436.Z0FBQUFBQmZXcWlpV0hCTjVVcF9uSmttLUNzeXJlR0t4Tkh5VW5aZVo2V2ttX19fcmxsWnZwd0lBcWpfNWJFMW41N2Jpck0tR2xNNTRFVjFEQXJMVmZRMkQ2N1BwdndVNGloNjJuSnM1ZEpxR1BkWXJXc013NkVFR1B2RElhQlFiekZGcUFDdV9Bc2o; Domain=reddit.com; Max-Age=7199; Path=/; expires=Fri, 11-Sep-2020 00:28:50 GMT; secure")(set-cookie"edgebucket=422e8tEzvhRq1XUlmY; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 597.0)(x-ratelimit-reset 70)(x-ratelimit-used 3)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/messaging.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 30 Aug 2020 12:10:50 GMT")(server snooserv)(set-cookie"edgebucket=hCWk9BHEJ3N1FjY0Jk; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/prefs/messaging?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 174))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 174)(content-type"application/json; charset=UTF-8")(date"Sun, 30 Aug 2020 12:10:50 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=dmdpkihnmqqcjecnlf.0.1598789450394.Z0FBQUFBQmZTNWRLQjdEUDdMckNVYmtqbEQ4eWVILXcyMHlYb0c1ejNPR1o4UGNhN2l6aWZrMkV1RXhHdExXcTBMSW1nWnJiRzJXdU9aU0EtT3pmNThRT0ZDdWE5MGNBVVNiTkc4eTRSQWxZX1lQS3JGdERIUk92ZGg5U3QxRHR0SVBEVS1TZUJVSk0; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 30-Aug-2020 14:10:50 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 30-Aug-2023 12:10:50 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=zBdb2n3MmnonywzZlE; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 550)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"[{\"kind\": \"UserList\", \"data\": {\"children\": []}}, {\"kind\": \"UserList\", \"data\": {\"children\": [{\"date\": 1598789198.0, \"rel_id\": \"r9_1vt1em\", \"name\": \"spez\", \"id\": \"t2_1w72\"}]}}]"))) -------------------------------------------------------------------------------- /test/cassettes/muted.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sat, 19 Sep 2020 22:30:59 GMT")(server snooserv)(set-cookie"edgebucket=zQojj2hqvtr9FPGCdY; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ThirdRealm/about/muted?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 227))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 227)(content-type"application/json; charset=UTF-8")(date"Sat, 19 Sep 2020 22:30:59 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=klklojaejdqaooghpc.0.1600554659336.Z0FBQUFBQmZab2FqZUg5YjRUai1QWmdEcUgtT0ZjQ3paRVc3M1haZlk1ZU1PMWpiUmNiNWV2QndlN3hCbXZ1aVp1X0tXMXBqOXlZb0ZNdkx2OGNoOXdzZ2l3UlB0Rmt6cm1fMnVsM0tKXzZPQjVtNVZyeXhRdFJtdmQxaUdUUVhjMkhQSURsLXFvWVU; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 20-Sep-2020 00:30:59 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Tue, 19-Sep-2023 22:30:59 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=wSQ4Hul76u1zVARzuM; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 541)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"Listing\", \"data\": {\"modhash\": null, \"dist\": null, \"children\": [{\"date\": 1600554641.0, \"rel_id\": \"Mute_c00caa20-fac7-11ea-a7ae-22f314d2d040\", \"name\": \"BJO_test_user\", \"id\": \"t2_xw1ym\"}], \"after\": null, \"before\": null}}"))) -------------------------------------------------------------------------------- /test/cassettes/oauth2_refresh_token__bad_token.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(refresh_token))(refresh_token())))))(response(((encoding(Fixed 40))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 40)(content-type"application/json; charset=UTF-8")(date"Mon, 16 Aug 2021 12:05:36 GMT")(server snooserv)(set-cookie"loid=0000000000dy6kaj1h.2.1629115536645.Z0FBQUFBQmhHbFNRQmhUY3FIbENfU1Q4dWJDZVBmRjg2cmdVM1ExcmNQZ0ZiU2ZYTWVSYW5Rcnl4WDZVT2FKWEtEUmlpZXMtUjNENHpjdGNiY3o4eXlHMjdjbmd0UDV3VWZvanRzSjE5VFpEZ25pWWtjdXRDS2FRTjNYd0NsMmM1aDEwWXR1R2lqdEM; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Wed, 16-Aug-2023 12:05:36 GMT; secure")(set-cookie"session_tracker=mZezrXm7BlvfIUmzaH.0.1629115536645.Z0FBQUFBQmhHbFNRUnN0M3JuUkZNT2d0SVNCOVM5Z05WMnlVSzd2a3dYSHpUVjlnYkI4RzhhOUprZFBQQnlmYzNBaTJhVmJSeFdVRTlWd3picmF4ZjBxUFFaZENTbXV5N2RvUnd0enMwNWhSRnNvT3ZlalBYanJldld1UUVORW4wRTd5WnJUaVE0WWo; Domain=reddit.com; Max-Age=7199; Path=/; expires=Mon, 16-Aug-2021 14:05:36 GMT; secure")(set-cookie"edgebucket=3mOacK9BpLNGpIFWmC; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-clacks-overhead"GNU Terry Pratchett")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 297)(x-ratelimit-reset 264)(x-ratelimit-used 3)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status Bad_request)(flush false))"{\"message\": \"Bad Request\", \"error\": 400}"))) -------------------------------------------------------------------------------- /test/cassettes/oauth2_refresh_token_insufficient_scope.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(refresh_token))(refresh_token())))))(response(((encoding(Fixed 184))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 184)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Jul 2021 13:48:26 GMT")(server snooserv)(set-cookie"edgebucket=MT16Yz3T0DIZGUBXCB; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-clacks-overhead"GNU Terry Pratchett")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 299)(x-ratelimit-reset 94)(x-ratelimit-used 1)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"refresh_token\": \"\", \"scope\": \"identity\"}")))((request(Get(uri https://oauth.reddit.com/api/info?raw_json=1&id=t3_odlsl2)(headers((authorization"bearer ")))))(response(((encoding(Fixed 38))(headers((accept-ranges bytes)(access-control-allow-origin *)(access-control-expose-headers X-Moose)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 38)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Jul 2021 13:48:26 GMT")(server snooserv)(set-cookie"loid=0000000000dijrnrio.2.1627220906977.Z0FBQUFBQmdfV3VxY0tER3dTQXZyakFWdnpuSjJFcW1jNmhLeG9CWWNUcHpXMXVTMlpNNXA5YWtYTjJoLWIwNWRDcU5tNFluTkF1cDZRZFZaRElPYzZNdjN6c2VFS01RbkFZMGpsa3AyLTZidElfRlJ1TkpKN2Nzdi1hUG8ycnIwUUxpUlB5eDRCZXg; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Tue, 25-Jul-2023 13:48:26 GMT; secure; SameSite=None; Secure")(set-cookie"session_tracker=GiIdHGp9KRBNycfn5d.0.1627220906977.Z0FBQUFBQmdfV3VxLUtmQ04tRzlVQlQ4dVVGNFVRWGx0Rm1qX2tqMzlYMERCTWZCWTR6bnV1Wk1na3ladXJMNmo5a1BxQnU4VHZ0aGNwT0RWbEE1NFcwWDdZV3VLMWx5Qlp6a3hJSlhfdXYyOUVMUjlUNlY0N3B3d3JaazBRbmhjS0NncVM1dERtOXI; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 25-Jul-2021 15:48:26 GMT; secure; SameSite=None; Secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=DOY2vtwecNrBUXjM3E; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(www-authenticate"Bearer realm=\"reddit\", error=\"insufficient_scope\"")(x-clacks-overhead"GNU Terry Pratchett")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status Forbidden)(flush false))"{\"message\": \"Forbidden\", \"error\": 403}"))) -------------------------------------------------------------------------------- /test/cassettes/random.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Wed, 02 Sep 2020 12:23:31 GMT")(server snooserv)(set-cookie"edgebucket=GDdTlPdOwlgeQY4Dh6; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ocaml/random?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 0))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 0)(content-type"application/json; charset=UTF-8")(date"Wed, 02 Sep 2020 12:23:32 GMT")(expires -1)(location https://www.reddit.com/r/ocaml/comments/feyhbv/what/.json?utm_campaign=redirect&utm_medium=desktop&utm_source=reddit&utm_name=random_link)(server snooserv)(set-cookie"session_tracker=fmnbkaodgddgdhigpa.0.1599049411998.Z0FBQUFBQmZUNDdFNzk3SmxxLS1QYWR0akR0U2tzYWhSZG9DcmdfYmdVblhGTnRxY0p0UFkxQkhNM1FmcWV5bVN4b284UUF4bWYtcy1pQ0dOY3VtQnJHekNfTlF2SmxCQlItcEdWa201QURpTWRpY0JWTWp0djJUa0wwZjAzd2hWcXQ3NmZCSTYzMU4; Domain=reddit.com; Max-Age=7199; Path=/; expires=Wed, 02-Sep-2020 14:23:32 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sat, 02-Sep-2023 12:23:32 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=uplFGI2PGMTK0SZ6D2; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 388)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status Found)(flush false))""))) -------------------------------------------------------------------------------- /test/cassettes/read_message.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:50:15 GMT")(server snooserv)(set-cookie"edgebucket=ddmuJnMlpkT7GphGwI; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/read_message)(headers((authorization"bearer ")))(params((raw_json(1))(id(t4_rdjz4y))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:50:15 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 03-Sep-2023 11:50:15 GMT; secure")(set-cookie"session_tracker=okanbrrcgioihhbacq.0.1599133815504.Z0FBQUFBQmZVTmgzSWZvM0NLeUtKa2NIelVONnp2OXZWT1lEQ1dhYi1Wd2ZUYXRPaU5aYzlsbW16ZDNaOXBhLWJBNzkxdFdCc3dxZE5TbElaeFg0b0ZPdzRkMy10NWpPVmdidEpncXZXMldLemVmbDdQNGxZSzhPUzlaZHduYV9pcWJhMjk3Z2htOGk; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 03-Sep-2020 13:50:15 GMT; secure")(set-cookie"edgebucket=Nl5UpPoBEnGz2KT0DL; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 585)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/remove.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 04 Oct 2020 13:36:46 GMT")(server snooserv)(set-cookie"edgebucket=93EymwLS6WAnqMEDHN; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/remove)(headers((authorization"bearer ")))(params((raw_json(1))(id(t3_j4z0ig))(spam(false))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Sun, 04 Oct 2020 13:36:46 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=keplkoflperqnmdgmn.0.1601818606579.Z0FBQUFBQmZlY191MUd1akxkd2JsaTQwRWJaOWtEdFpnVl9UUjNZVkdXeEhwZDJhdnJDeXZXOEszTmNjVkg1b0dzTlcwWUZVd2V5Mi1OM2VEU2d1eWp3aWkzZjN5aXNmVjcwc2VDOFZQQjVKMnNJSkdVSHdnUU5SM3NVQUY2OVBkMmc3Q1ZKWXdZQXg; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 04-Oct-2020 15:36:46 GMT; secure")(set-cookie"edgebucket=IfxQy1cu2kRRFidZyP; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 194)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/remove_wiki_editor.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:03:08 GMT")(server snooserv)(set-cookie"edgebucket=3DtIgETowks1tJ2rSF; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/wiki/alloweditor/del)(headers((authorization"bearer ")))(params((raw_json(1))(page(index))(username(L72_Elite_Kraken))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:03:08 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 25-Oct-2023 15:03:08 GMT; secure")(set-cookie"session_tracker=mfmgqradgdfmaeodcg.0.1603638188746.Z0FBQUFBQmZsWk9zdFRrQXVLeVF6REdSQmJMS1R2WmVQOWNtTG5xRzNST2NadERibkxxX1RhVDZHTWk3R0V3VFhnZGZHcjgyaUI2V2drQ0xwZU4zVWtUczNySDExbHc1UWdTQ0NzNnVGQkFDOXVDd3ZFREEweTdoU1llZkd0M1lYeG1vWndqaVN6TXU; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 25-Oct-2020 17:03:08 GMT; secure")(set-cookie"edgebucket=DlCYZabm9CC2MYusev; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 597.0)(x-ratelimit-reset 412)(x-ratelimit-used 3)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/report.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sat, 11 Jul 2020 11:54:22 GMT")(server snooserv)(set-cookie"edgebucket=bOdkAhXIv6mahgkypQ; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-cache MISS)(x-cache-hits 0)(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-served-by cache-bos4637-BOS)(x-timer S1594468462.213225,VS0,VE321)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/report)(headers((authorization"bearer ")))(params((raw_json(1))(thing_id(t3_hony5b))(api_type(json))(reason("Test report"))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Sat, 11 Jul 2020 11:54:24 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=ihcgddlpqraarnjofp.0.1594468462673.Z0FBQUFBQmZDYWh3ZGRuLWF6cUJzSzlRR2pvcERzRk52S2N1dkhDTWhqXzlPbEFZa2Jub20zckowRGV5N0gteldhZFRfbWZNYnJhOFdWdW05aGhadmtITm5UdzJvREYzNTVleDNyRndpTE1yUnUzTDFIWGVQNmczczZDNDNRYzBUSXdwYmJJRjJsN0Y; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sat, 11-Jul-2020 13:54:24 GMT; secure")(set-cookie"edgebucket=f2WjSnqKT8E88VpxM1; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-cache MISS)(x-cache-hits 0)(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 338)(x-ratelimit-used 1)(x-served-by cache-bos4634-BOS)(x-timer S1594468463.629564,VS0,VE1881)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/revert_wiki_page.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:30:02 GMT")(server snooserv)(set-cookie"edgebucket=LdXtcDqLc8fqMVlpK0; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/wiki/revert)(headers((authorization"bearer ")))(params((raw_json(1))(page(index))(revision(e4d3d130-52b9-11e7-9d0c-0e1b806ed802))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:30:02 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 25-Oct-2023 15:30:02 GMT; secure")(set-cookie"session_tracker=qeforibgopddanoekf.0.1603639802576.Z0FBQUFBQmZsWm42b2xsMms4MUZOamZWN1FjRzlqdXlxU0RyQktSRXhyVVhoS09hbnVNQk9zcGowNVQ4VkdSN25WLUNZSm4zV1RiRU9MbUw5MFYyZU5CWDNKVFpfalJRQTd6bjJMM3JleUxMWlJtVE9Hek1ybTlZMGN1OEk1QVg1ai02MDNENS1DQkc; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 25-Oct-2020 17:30:02 GMT; secure")(set-cookie"edgebucket=3Y4mdrs0pD9zYnluo9; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 598)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/save.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Tue, 01 Sep 2020 12:47:49 GMT")(server snooserv)(set-cookie"edgebucket=sNNSY4RrjszCw8oYdn; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/save)(headers((authorization"bearer ")))(params((raw_json(1))(id(t1_g3krlj5))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Tue, 01 Sep 2020 12:47:50 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Fri, 01-Sep-2023 12:47:50 GMT; secure")(set-cookie"session_tracker=kfbraloeboojpgqjrr.0.1598964470089.Z0FBQUFBQmZUa0wyWVZiV19aUWxOR0dUWmlKUWQ1dmc2dngyckdPZ2sxRTZYUzl4SnU5YkZYejB2dDl1SVI0RU1aZDRjQ1VfclJCbzVBUDVCaWJ4Y21tcGk5bkFZOEI2bUZiVXZhdWxqTjI1UC00UkRSTHB2NnQ3U1pfd2dUYUFubEE3ZVhZVV9FTHQ; Domain=reddit.com; Max-Age=7199; Path=/; expires=Tue, 01-Sep-2020 14:47:50 GMT; secure")(set-cookie"edgebucket=0v9c74rBeFfb1BQex6; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 130)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/search_subreddits_by_name.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Tue, 22 Sep 2020 13:06:24 GMT")(server snooserv)(set-cookie"edgebucket=uC5DmXQBpizxWojcQe; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/api/search_reddit_names?raw_json=1&query=python)(headers((authorization"bearer ")))))(response(((encoding(Fixed 166))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 166)(content-type"application/json; charset=UTF-8")(date"Tue, 22 Sep 2020 13:06:24 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=mmfoieqpnghccbqpcq.0.1600779984268.Z0FBQUFBQmZhZmJRWjFYWHVlcWFBbXVWTF9CTFZBaHBRS1JjaEdBQ1JkR1o4WFJKaEJxOVAtNEtnb1o0WF9tWkFyMHB4OGt3RGtyMjA2a096SDhPbDZveHZ6TldQQmRJMURiVnNiSzI0dEZLTExrcGxtZHNJWlNzUnk3LXFfaU50YnhMbklpa3B4RHY; Domain=reddit.com; Max-Age=7199; Path=/; expires=Tue, 22-Sep-2020 15:06:24 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Fri, 22-Sep-2023 13:06:24 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=sb5cW2yZTCuRhulgO1; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 596.0)(x-ratelimit-reset 216)(x-ratelimit-used 4)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"names\": [\"Python\", \"pythontips\", \"PythonProjects2\", \"pythonforengineers\", \"python_netsec\", \"PythonJobs\", \"pythoncoding\", \"PythonGUI\", \"PythonNoobs\", \"pythonclass\"]}"))) -------------------------------------------------------------------------------- /test/cassettes/set_subreddit_stylesheet.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Fri, 25 Sep 2020 12:39:07 GMT")(server snooserv)(set-cookie"edgebucket=uQ3xsj2SFpgyHoMahR; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/subreddit_stylesheet)(headers((authorization"bearer ")))(params((raw_json(1))(api_type(json))(op(save))(stylesheet_contents("body {font-family: monospace}"))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Fri, 25 Sep 2020 12:39:08 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Mon, 25-Sep-2023 12:39:07 GMT; secure")(set-cookie"session_tracker=gjjkjkalaqorcagknr.0.1601037547924.Z0FBQUFBQmZiZVRzZk5TQlJITndzc1QzTndTcnJjOVhXWVg5eFlnQ0pVXzJrRmJONHJJdVRZQ3VWbWR4MTNPcmpUbmdhd0M3ajJrbmRadTZfMXMwTmVHMlRGbENUX1BsVWRmR3QzZy10S0pFOHRHeVlCeWlsT1h0NjdQMnRTVWU5c2ZBd09yNWs4ejM; Domain=reddit.com; Max-Age=7199; Path=/; expires=Fri, 25-Sep-2020 14:39:08 GMT; secure")(set-cookie"edgebucket=cO9SBzLJAVvwrB05AK; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 595.0)(x-ratelimit-reset 53)(x-ratelimit-used 5)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/set_suggested_sort.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 09 Jul 2020 00:04:15 GMT")(server snooserv)(set-cookie"edgebucket=YKx0Gfjt5AOCgXsu0t; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-cache MISS)(x-cache-hits 0)(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-served-by cache-ewr18148-EWR)(x-timer S1594253055.483603,VS0,VE361)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/set_suggested_sort)(headers((authorization"bearer ")))(params((raw_json(1))(api_type(json))(id(t3_hmjghn))(sort(new))))))(response(((encoding(Fixed 24))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 24)(content-type"application/json; charset=UTF-8")(date"Thu, 09 Jul 2020 00:04:16 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 09-Jul-2023 00:04:15 GMT; secure")(set-cookie"session_tracker=gllolircphhboppkom.0.1594253055947.Z0FBQUFBQmZCbDhBRUs0cHgzejVJUjlkeF9ydFhkTGRBeFQxb202ZWFaVV9TQ0hxNE9KSzZpd2lfSGg1WDE2X1hFR0x3SGRaZDd0NkFlRnNXMUs1TVdXTWZqUWI5a0I1b0dxZHRNUGJiUEMwNThrc21mMTVPQVg1Z3NPeVlicXE2cnhKWUpLa2V2cTA; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 09-Jul-2020 02:04:16 GMT; secure")(set-cookie"edgebucket=Ds1TVRnE8tlTDxzFMU; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-cache MISS)(x-cache-hits 0)(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 345)(x-ratelimit-used 1)(x-served-by cache-ewr18154-EWR)(x-timer S1594253056.905569,VS0,VE155)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": []}}"))) -------------------------------------------------------------------------------- /test/cassettes/spoiler.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Tue, 01 Sep 2020 13:08:14 GMT")(server snooserv)(set-cookie"edgebucket=DGuwBRyJOeQTE4oSw4; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/spoiler)(headers((authorization"bearer ")))(params((raw_json(1))(id(t3_hofd3k))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Tue, 01 Sep 2020 13:08:14 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Fri, 01-Sep-2023 13:08:14 GMT; secure")(set-cookie"session_tracker=karifiopflnprlmfgo.0.1598965694636.Z0FBQUFBQmZUa2UtMWUtNWtGRUtWVkZLa1VoaTdsNUJFYmk2VnBpUUptN3V6UkJQbmoxaDJ2LUhJN21iVHpUcGFiR0JFaVMzdXBmSFRCdXFBcjhQVmt2ak1YZDRncDRMVzQxWGxqd0tNTnZ6ZHFjS3B0dkdyc09nbG91YWN6cFZON2VVYUhCSlVqWEw; Domain=reddit.com; Max-Age=7199; Path=/; expires=Tue, 01-Sep-2020 15:08:14 GMT; secure")(set-cookie"edgebucket=aXV6gjhAAZIEJBluXP; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 594.0)(x-ratelimit-reset 106)(x-ratelimit-used 6)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/stylesheet.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 10 Sep 2020 22:38:21 GMT")(server snooserv)(set-cookie"edgebucket=LD1LlymmA7Du3Bu802; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/Thirdrealm/about/stylesheet?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 261))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 261)(content-type"application/json; charset=UTF-8")(date"Thu, 10 Sep 2020 22:38:22 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=iqoalicelajonmjlcj.0.1599777502154.Z0FBQUFBQmZXcXJlaDNwekttVWwxMTlVVDgzZmtMNWt1QUVZMWc5dnVhcEQySjg4ekZWWG9NR2JYMDRPX0p1dWplYXdNNTRvMzFtR2RYYmJvby1pcFZxcnVjMHRUazUyRnlhYzZ0dTdlNDhZdkh4VEhsVVdLS3R3MVlfaUx3NG5oX0pXNlBlV0N6Z1Q; Domain=reddit.com; Max-Age=7199; Path=/; expires=Fri, 11-Sep-2020 00:38:22 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 10-Sep-2023 22:38:22 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=jRXidRKKo2e5Auo6wi; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 595.0)(x-ratelimit-reset 98)(x-ratelimit-used 5)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"stylesheet\", \"data\": {\"images\": [{\"url\": \"https://b.thumbs.redditmedia.com/A0DPScGn6-2Bg_BbgC1vJQMcbuygqOAxqtxx9z7cjLM.jpg\", \"link\": \"url(%%leviroth%%)\", \"name\": \"leviroth\"}], \"subreddit_id\": \"t5_390u2\", \"stylesheet\": \"body {font-family: monospace}\"}}"))) -------------------------------------------------------------------------------- /test/cassettes/submit.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Mon, 06 Jul 2020 23:47:44 GMT")(server snooserv)(set-cookie"edgebucket=oDTDEaPpwF3bw8lyaC; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-cache MISS)(x-cache-hits 0)(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-served-by cache-ewr18149-EWR)(x-timer S1594079265.668537,VS0,VE313)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/submit)(headers((authorization"bearer ")))(params((raw_json(1))(kind(self))(text("This is a post body."))(api_type(json))(sr(ThirdRealm))(title("Test post title"))))))(response(((encoding(Fixed 169))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 169)(content-type"application/json; charset=UTF-8")(date"Mon, 06 Jul 2020 23:47:45 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=kqlegfgegnohboaqbn.0.1594079265083.Z0FBQUFBQmZBN2doc3ZXZ3B1dFBmaWtYRHBYbG1YaGxab0taMGtpNUJLTk9tZ01tUDUtaEJ1ZHptajhzZTByWjdEMnJaZnBZMEFBU2J5dm1VaWFqazVWNHl5c3IxTlRGR28xeWhHV09jNUt0dy1hYmRYeWxrMWNNUFhWeXV3NnBHcFZaZ1gwNng2TWc; Domain=reddit.com; Max-Age=7199; Path=/; expires=Tue, 07-Jul-2020 01:47:45 GMT; secure")(set-cookie"edgebucket=Hm0tIUZQfmIbCq0kRF; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-cache MISS)(x-cache-hits 0)(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 597.0)(x-ratelimit-reset 135)(x-ratelimit-used 3)(x-served-by cache-ewr18154-EWR)(x-timer S1594079265.033302,VS0,VE249)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": [], \"data\": {\"url\": \"https://www.reddit.com/r/ThirdRealm/comments/hmjghn/test_post_title/\", \"drafts_count\": 0, \"id\": \"hmjghn\", \"name\": \"t3_hmjghn\"}}}"))) -------------------------------------------------------------------------------- /test/cassettes/submit__crosspost.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Wed, 02 Sep 2020 23:28:12 GMT")(server snooserv)(set-cookie"edgebucket=oyyHoOKVsOnHNS2DJX; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/submit)(headers((authorization"bearer ")))(params((raw_json(1))(kind(crosspost))(crosspost_fullname(t3_box80e))(api_type(json))(sr(ThirdRealm))(title(Crosspost))))))(response(((encoding(Fixed 163))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 163)(content-type"application/json; charset=UTF-8")(date"Wed, 02 Sep 2020 23:28:12 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sat, 02-Sep-2023 23:28:12 GMT; secure")(set-cookie"session_tracker=ojldcomjmmgrlqelle.0.1599089292127.Z0FBQUFBQmZVQ3FNcFhPcElOSEVuOUZjZGRScFhSOHR4OTc2azVIVHZaRnRCN3I3dTVXNF9SUzA0U3JFTEpqaXhwWnM5akN3NGZVTGVNUy1ubW1ucmFIU296LXRfWkVqcWxIVTBDc0VSRTh5LVUwelFMNk1jSjlKV3VrbU1BQjBKT1ZWeXFMUDI1M3I; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 03-Sep-2020 01:28:12 GMT; secure")(set-cookie"edgebucket=PEB7eUc7JpSj4QGnb8; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 108)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"json\": {\"errors\": [], \"data\": {\"url\": \"https://www.reddit.com/r/ThirdRealm/comments/ili4vc/crosspost/\", \"drafts_count\": 0, \"id\": \"ili4vc\", \"name\": \"t3_ili4vc\"}}}"))) -------------------------------------------------------------------------------- /test/cassettes/subreddit_sticky.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sat, 24 Oct 2020 21:15:22 GMT")(server snooserv)(set-cookie"edgebucket=SrSGA4kEiCPYmtY8gr; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ThirdRealm/about/sticky?raw_json=1&num=2)(headers((authorization"bearer ")))))(response(((encoding(Fixed 0))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 0)(content-type"application/json; charset=UTF-8")(date"Sat, 24 Oct 2020 21:15:22 GMT")(expires -1)(location https://oauth.reddit.com/r/ThirdRealm/comments/jhgtn4/this_is_a_link/.json)(server snooserv)(set-cookie"session_tracker=qfapbibfaeabjrdcij.0.1603574122817.Z0FBQUFBQmZsSmxxWXJNelpxbm5MdmotekI0bkFEdDV0RXBFdHVzeGdpampoS1hmdDNlLTI2NEg5TmlmZE1sN3dmSzRGS01Sb01NT05JY3JURTYtLXZ5LUlmTzFVYWRzbWlVTkd2YVFOZ0xVZjhJWDNONy1OU2JPMHBQdnBNY1JmWldsLWtoZWlncU8; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sat, 24-Oct-2020 23:15:22 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Tue, 24-Oct-2023 21:15:22 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=jZlR0lthScV8iNn61H; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 278)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status Found)(flush false))""))) -------------------------------------------------------------------------------- /test/cassettes/subscribe__by_id.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Fri, 25 Sep 2020 12:58:10 GMT")(server snooserv)(set-cookie"edgebucket=sW421VwLa5LBhPgC2j; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/subscribe)(headers((authorization"bearer ")))(params((raw_json(1))(action(sub))(sr(t5_2qh0y))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Fri, 25 Sep 2020 12:58:10 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Mon, 25-Sep-2023 12:58:10 GMT; secure")(set-cookie"session_tracker=hcqmmkgolpfgcgflcr.0.1601038690170.Z0FBQUFBQmZiZWxpSFlSTE1PTkdvcDlvSWtQTUdJenZDczY3elJkWXhrY1pCRkpyUHExdEJJNlZEb2dfYnFxRURNYjNfQ2hSUUlfRHozUjZFOGY2QjBsRVJUbUkwa3dxLV96YVZXTHNNN2hiZnRSbENqem10QnV0aVdQc0pkTnV6dGNPdlRDYU1IV2E; Domain=reddit.com; Max-Age=7199; Path=/; expires=Fri, 25-Sep-2020 14:58:10 GMT; secure")(set-cookie"edgebucket=iQNiOiu5fzpz8Xd0gQ; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 597.0)(x-ratelimit-reset 110)(x-ratelimit-used 3)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/subscribe__by_name.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Fri, 25 Sep 2020 12:55:39 GMT")(server snooserv)(set-cookie"edgebucket=QiaBuN6Lypq0J5KR3x; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/subscribe)(headers((authorization"bearer ")))(params((raw_json(1))(action(sub))(sr_name(python))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Fri, 25 Sep 2020 12:55:39 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Mon, 25-Sep-2023 12:55:39 GMT; secure")(set-cookie"session_tracker=lioekgfoejjmjkfljq.0.1601038539555.Z0FBQUFBQmZiZWpMNUVKLUQzb2NHZDBOcWRMQmVhb3hZNnlMSVlFY05MWHAtSUE1TzRXMVhUWENyUHJjSi1nd0t4SWRqeUhNOWxVMjFTVGFqRjJvaGdWU2NHUjluTnRhZVYzY3owenhaUWhkTWNQc1JGY0xWYVZjT0t2R2R5T25ScXU1ODhEcnpHal8; Domain=reddit.com; Max-Age=7199; Path=/; expires=Fri, 25-Sep-2020 14:55:39 GMT; secure")(set-cookie"edgebucket=DejYs9Asy90cTxvvqw; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 261)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/toggle_wiki_revision_visibility.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:19:33 GMT")(server snooserv)(set-cookie"edgebucket=OmHhTJGTC9FSgowfpV; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/r/ThirdRealm/api/wiki/hide)(headers((authorization"bearer ")))(params((raw_json(1))(page(index))(revision(8048c97c-52ba-11e7-ab00-0ad38c20ef7e))))))(response(((encoding(Fixed 16))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 16)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 15:19:33 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 25-Oct-2023 15:19:33 GMT; secure")(set-cookie"session_tracker=mhpdjmqnbmlpepjgiq.0.1603639173262.Z0FBQUFBQmZsWmVGbzhjZDRmd1BvOUFCMmQyTHRGODV4d3VxWWVUc3N1SEZ4dngtMEE2TFRwTGZKZ3hPUEJWbktZN0dwMXp0dTBzWUd3V3ZxZWRxNjFkVzJqQ1BIYUlpU1VpbzZtdXhhanVGejN3S3pmUHBCaXFLVXJnTzdWSmdzUHlJbVhOZHdmeXQ; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 25-Oct-2020 17:19:33 GMT; secure")(set-cookie"edgebucket=S8LzniS2MRjQwlVaGd; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 27)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"status\": true}"))) -------------------------------------------------------------------------------- /test/cassettes/trophies.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 117))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 117)(content-type"application/json; charset=UTF-8")(date"Sun, 10 Jan 2021 01:54:13 GMT")(server snooserv)(set-cookie"edgebucket=2ETQ4KmWhdON4lV5sq; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/api/v1/me/trophies?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 921))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 921)(content-type"application/json; charset=UTF-8")(date"Sun, 10 Jan 2021 01:54:13 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=fcnokkbahkhgcfjiob.0.1610243653182.Z0FBQUFBQmYtbDVGcXpEVWVYVGNnbTN6VER1N0E0Tk51UmV1U2d6d3VMeHRJcDBBS21EcmxydWpRUl9GVUtLazRnY0k1ZXB1bkRyRFNSZEtodDNUanU4bEFLczF4WlNuNWpaOXdfT3dpN1gxY0tMczIxcnhnQUFCNWpGTC1ndWpqeGxrcFF3WXlJWFU; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 10-Jan-2021 03:54:13 GMT; secure; SameSite=None; Secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=VBBahxNTCmotAdUDnh; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(vary accept-encoding)(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 347)(x-ratelimit-used 1)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"TrophyList\", \"data\": {\"trophies\": [{\"kind\": \"t6\", \"data\": {\"icon_70\": \"https://www.redditstatic.com/awards2/reddit_gold-70.png\", \"granted_at\": 1609678143, \"url\": \"/premium\", \"icon_40\": \"https://www.redditstatic.com/awards2/reddit_gold-40.png\", \"name\": \"Reddit Premium\", \"award_id\": \"v\", \"id\": \"2pgty2\", \"description\": \"Since January 2021\"}}, {\"kind\": \"t6\", \"data\": {\"icon_70\": \"https://www.redditstatic.com/awards2/3_year_club-70.png\", \"granted_at\": 1585935110, \"url\": null, \"icon_40\": \"https://www.redditstatic.com/awards2/3_year_club-40.png\", \"name\": \"Three-Year Club\", \"award_id\": null, \"id\": null, \"description\": null}}, {\"kind\": \"t6\", \"data\": {\"icon_70\": \"https://www.redditstatic.com/awards2/verified_email-70.png\", \"granted_at\": null, \"url\": null, \"icon_40\": \"https://www.redditstatic.com/awards2/verified_email-40.png\", \"name\": \"Verified Email\", \"award_id\": \"o\", \"id\": \"1qr5eq\", \"description\": null}}]}}"))) -------------------------------------------------------------------------------- /test/cassettes/trusted.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 30 Aug 2020 12:06:55 GMT")(server snooserv)(set-cookie"edgebucket=zwYVrBqcnUc1q5bUGp; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/prefs/trusted?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 124))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 124)(content-type"application/json; charset=UTF-8")(date"Sun, 30 Aug 2020 12:06:55 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=cnlcjkfchrobchblik.0.1598789215760.Z0FBQUFBQmZTNVpmM3Y0djNlYVNsQnpPNUtNOWRLT3pBV200UkJoOXZvMktLVTJybTRPdzNfdmJ6Q3NtcnJ1bGhLOXRIOFVvVXFPT01iRHVtbE1JZkhSMlB5TFh4QkN1SWwzcktxZ19SeGVDdDlUWGxKTVplZjBraW9BbUQtampBUlFsSkE0SFRkeFI; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 30-Aug-2020 14:06:55 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 30-Aug-2023 12:06:55 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=lpqnVwvRwYw381dRBJ; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 185)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"UserList\", \"data\": {\"children\": [{\"date\": 1598789198.0, \"rel_id\": \"r9_1vt1em\", \"name\": \"spez\", \"id\": \"t2_1w72\"}]}}"))) -------------------------------------------------------------------------------- /test/cassettes/uncollapse_message.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:41:49 GMT")(server snooserv)(set-cookie"edgebucket=XhqAsnZsPnlkl4UbHi; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/uncollapse_message)(headers((authorization"bearer ")))(params((raw_json(1))(id(t4_rdjz4y))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:41:49 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 03-Sep-2023 11:41:49 GMT; secure")(set-cookie"session_tracker=aplberlgcclimefapp.0.1599133309422.Z0FBQUFBQmZVTlo5ZVVQVFRUQkVwXzI2MGF3UFBFR0ZTckpneTlUTTZyc3h6elg0M1RJSHF2WlBlZVU2ckg4cWdxcjNTa012RWxsakcxSzFENzVzVk4zZDIweWNIakwxSFBzdElwYkFkaWwyZm5UQ2Q2Q3gzUEY0X1Z6NUJaQXdaUjU3SFZ6M2RQeDc; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 03-Sep-2020 13:41:49 GMT; secure")(set-cookie"edgebucket=sFeEgj69sVZUFzMuJx; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 491)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/unignore_reports.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Wed, 09 Sep 2020 11:27:08 GMT")(server snooserv)(set-cookie"edgebucket=tPnVJUFgDXvtR0Cqjp; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/unignore_reports)(headers((authorization"bearer ")))(params((raw_json(1))(id(t3_ili4vc))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Wed, 09 Sep 2020 11:27:08 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sat, 09-Sep-2023 11:27:08 GMT; secure")(set-cookie"session_tracker=memdbncdbdcbiqijfq.0.1599650828685.Z0FBQUFBQmZXTHdNVi1tOWJ3cXdTRDMwOVkzWG9VNG5MYUI0Wmd6ZnFsa2M0MjNpblRaNWRPR21lbUNGUHBuZlZpWDJLQ0VBZFgxcUZKN1VXdlJRVUVZNFZkaG90NVA5cGhnWnJaVHhBRzdaU2lCUGNGdXB6WERWdW5LdXVCdnRfM0U5dmxONnRhS3k; Domain=reddit.com; Max-Age=7199; Path=/; expires=Wed, 09-Sep-2020 13:27:08 GMT; secure")(set-cookie"edgebucket=Wuf8sh1SFJDOiY4sut; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 172)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/unread_message.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:48:00 GMT")(server snooserv)(set-cookie"edgebucket=jywMh6AS3qd3HooDS0; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/unread_message)(headers((authorization"bearer ")))(params((raw_json(1))(id(t4_rdjz4y))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Thu, 03 Sep 2020 11:48:00 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Sun, 03-Sep-2023 11:48:00 GMT; secure")(set-cookie"session_tracker=rppmndqhbkiaqmagcb.0.1599133680534.Z0FBQUFBQmZVTmZ3aXdsRWNhVkRBN292Z3BHMmNWRHdHYTMyZnVUNHVaR3UzdkY0NkQ1STBFSTBod3lBRzIwTEdCY28xOVFlbW1VS2ZYb25UOE9SX1N5d3hXZVVKWXREQzdRcWNyMlhYVDBKcWRyRE0xMlB5WjR2NTBzbGRpanpna0lPX3k1RG1BUks; Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 03-Sep-2020 13:48:00 GMT; secure")(set-cookie"edgebucket=qELRgaLWAOFXBF2jbE; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 597.0)(x-ratelimit-reset 120)(x-ratelimit-used 3)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/unspoiler.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Tue, 01 Sep 2020 13:10:18 GMT")(server snooserv)(set-cookie"edgebucket=csJ8xA57CYE8le4iqH; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Post_form(uri https://oauth.reddit.com/api/unspoiler)(headers((authorization"bearer ")))(params((raw_json(1))(id(t3_hofd3k))))))(response(((encoding(Fixed 2))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 2)(content-type"application/json; charset=UTF-8")(date"Tue, 01 Sep 2020 13:10:18 GMT")(expires -1)(server snooserv)(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Fri, 01-Sep-2023 13:10:18 GMT; secure")(set-cookie"session_tracker=ganpgnkimbdjdeeign.0.1598965818710.Z0FBQUFBQmZUa2c2dTc3dFM0d1BLekVWSUJzdWxnbU14ZjhreFBhb3hRTDg4elNCdkNhS2JHRG5ITDVuUU1sbmUyVGRUWUVXc2hNOU5tb3VaWjJJYVNRV0U1Mkc4dE5tVWEzV3NBVXJ5NWV2ZWJtaThtY29DREdyWmthQ3U5ZjBLX3ZnbjkwalduUlM; Domain=reddit.com; Max-Age=7199; Path=/; expires=Tue, 01-Sep-2020 15:10:18 GMT; secure")(set-cookie"edgebucket=bGFsapBWJJWHV2LFcn; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 582)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false)){}))) -------------------------------------------------------------------------------- /test/cassettes/user_trophies__nonexistent_user.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 117))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 117)(content-type"application/json; charset=UTF-8")(date"Sun, 10 Jan 2021 01:43:36 GMT")(server snooserv)(set-cookie"edgebucket=ZEZ77U0pKYTfrbEWGG; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/api/v1/user/thisusershouldnotexist/trophies?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 117))(headers((accept-ranges bytes)(access-control-allow-origin *)(access-control-expose-headers X-Moose)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 117)(content-type"application/json; charset=UTF-8")(date"Sun, 10 Jan 2021 01:43:36 GMT")(expires -1)(server snooserv)(set-cookie"loid=00000000009qwqjgvj.2.1610243016983.Z0FBQUFBQmYtbHZJUU9hRUFUWFZjODZtYXpQc3ZmcEFBRGlhUmJ6SHVUcHZkaWhnSlYzLTdGX1lWeFBKdXBidUFHWkNGcDlyWjUwYldZRm9pY1hzSmdRVDUtc29oU1dRVTZuN2xORV9aRUw3c1BuREVoTTc0QWoxTUc5QWNJVzhpV3FBRld6MXd5cGM; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Tue, 10-Jan-2023 01:43:36 GMT; secure; SameSite=None; Secure")(set-cookie"session_tracker=lelclkkkproocadhjk.0.1610243016953.Z0FBQUFBQmYtbHZJc1BPR0JwU2NQSGZmUmQzWEFKUjcteG9uZWpjQnRvamdkVHFRblh4RkFXNFkyOGZQQjhILVdGUWgwOUtuMVF5RUR2Q2VtU292NGdTZkFGMHhRM0VtMWM1ek0yaS1JYzFSLXBYRVJEWGE4alNYclZIYlg1TURsZ1U2NWRLd2xPOXM; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 10-Jan-2021 03:43:36 GMT; secure; SameSite=None; Secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=KcuBWSGAMYDPIP0qGP; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 384)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status Bad_request)(flush false))"{\"fields\": [\"id\"], \"explanation\": \"that user doesn't exist\", \"message\": \"Bad Request\", \"reason\": \"USER_DOESNT_EXIST\"}"))) -------------------------------------------------------------------------------- /test/cassettes/user_upvoted.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((Authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 117))(headers((Connection keep-alive)(Content-Length 117)(Content-Type"application/json; charset=UTF-8")(x-frame-options SAMEORIGIN)(x-content-type-options nosniff)(x-xss-protection"1; mode=block")(x-ratelimit-remaining 299)(x-ratelimit-used 1)(x-ratelimit-reset 598)(cache-control"max-age=0, must-revalidate")(X-Moose majestic)(Accept-Ranges bytes)(Date"Mon, 28 Mar 2022 11:20:03 GMT")(Via"1.1 varnish")(Set-Cookie"edgebucket=kS4k7oYKp7ZogUYscS; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(Strict-Transport-Security"max-age=15552000; includeSubDomains; preload")(Server snooserv)))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/user/spez/upvoted?raw_json=1)(headers((Authorization"bearer ")))))(response(((encoding(Fixed 38))(headers((Connection keep-alive)(Content-Length 38)(Content-Type"application/json; charset=UTF-8")(x-ua-compatible IE=edge)(x-frame-options SAMEORIGIN)(x-content-type-options nosniff)(x-xss-protection"1; mode=block")(expires -1)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(x-ratelimit-remaining 599.0)(x-ratelimit-used 1)(x-ratelimit-reset 597)(access-control-allow-origin *)(access-control-expose-headers X-Moose)(X-Moose majestic)(Accept-Ranges bytes)(Date"Mon, 28 Mar 2022 11:20:03 GMT")(Via"1.1 varnish")(Set-Cookie"loid=0000000000000xw1ym.2.1463096550337.Z0FBQUFBQmlRWm5qczN3Nk9rUURTdXgydk03THFtUmQtbkpPd1NGR3MtRzZyV2xfUlhTcnNyd2VnR2daTE9oYmRPWUdVMm5RMUJpcVVyVnBkdFNZZ25CNUpNWGEwLVNtMVZ1aDhaUXk5WEZrYmZXRWlUaXl4U1FlakpZNjlOZE85S0c1aGtMWjFQNEc; Domain=reddit.com; Max-Age=63071999; Path=/; expires=Wed, 27-Mar-2024 11:20:03 GMT; secure; SameSite=None; Secure")(Set-Cookie"session_tracker=dfkcfmoqciacjhbldm.0.1648466403464.Z0FBQUFBQmlRWm5qS2loS0wtSkw4STBkWEYwRVFrUVBJY0RxRHJNRU52bkI4blVFQldsZEEzYWVlaUtVTXFEaS1KUTQteC1aanNqZWVySmVZTi1CSXdiOW1QQXAwNlNZeWExMmctZ1hMWG1WN2FvOWlVeUFzMFJzSGk3NjVLVmRodGdBVDItQlRCeTI; Domain=reddit.com; Max-Age=7199; Path=/; expires=Mon, 28-Mar-2022 13:20:03 GMT; secure; SameSite=None; Secure")(Set-Cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Thu, 27-Mar-2025 11:20:03 GMT; secure")(Set-Cookie"csv=2; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(Set-Cookie"edgebucket=Wq9GiBPaoyej3uJnTO; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(Strict-Transport-Security"max-age=15552000; includeSubDomains; preload")(Server snooserv)))(version HTTP_1_1)(status Forbidden)(flush false))"{\"message\": \"Forbidden\", \"error\": 403}"))) -------------------------------------------------------------------------------- /test/cassettes/wiki_banned.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sat, 19 Sep 2020 22:42:36 GMT")(server snooserv)(set-cookie"edgebucket=Ck2FlMjdW5Lzq9rqUP; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ThirdRealm/about/wikibanned?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 227))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 227)(content-type"application/json; charset=UTF-8")(date"Sat, 19 Sep 2020 22:42:36 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=hplqfqdcalbcooarci.0.1600555356333.Z0FBQUFBQmZab2xjaFpBcGhKMVV3U1VBZk14ek5DQ0g0ZEwxQjZIVi0xR29HMWhXS0M0Z0VOcHRyM3h6YUp1c1VhakhsU1hBdzJUUTdOMTlIVGNGN3FzYnVtTXlZbEtJRG9NMzNNNS1zUzlaaWZsaXkzYjBYUkFsa1RwbjI3Z3o0VWJtd1c5THp2RVE; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 20-Sep-2020 00:42:36 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Tue, 19-Sep-2023 22:42:36 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=iM9qJefXBTjqaZesCG; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 444)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"Listing\", \"data\": {\"modhash\": null, \"dist\": null, \"children\": [{\"name\": \"BJO_test_user\", \"days_left\": 2, \"note\": \"bar\", \"date\": 1600555145.0, \"rel_id\": \"rb_276wzg3\", \"id\": \"t2_xw1ym\"}], \"after\": null, \"before\": null}}"))) -------------------------------------------------------------------------------- /test/cassettes/wiki_contributors.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 20 Sep 2020 16:45:31 GMT")(server snooserv)(set-cookie"edgebucket=M5suOaSYFgnpD62OTH; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ThirdRealm/about/wikicontributors?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 200))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 200)(content-type"application/json; charset=UTF-8")(date"Sun, 20 Sep 2020 16:45:31 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=phmjcafgkecagioogm.0.1600620331741.Z0FBQUFBQmZaNGNyaTV3MEYwT3VWM0JwTTRpNlh1LVNSZWdGZEIwOEZHY0hHRHFVVHQ2UmhRMkRkN3l4clI1SU1FV2lQWVBXY0J1d21VS1pkTmIzbHFKU1ZjVWRfcmQ5bzdpMVVTUGV2aFJBSVFiVTFyRGVXSnZpOUNNMzR5a0dab0t3bXF2U2pNbDk; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 20-Sep-2020 18:45:31 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 20-Sep-2023 16:45:31 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=5VGG57eYMQwJgw09fI; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 598.0)(x-ratelimit-reset 269)(x-ratelimit-used 2)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"Listing\", \"data\": {\"modhash\": null, \"dist\": null, \"children\": [{\"date\": 1600620327.0, \"rel_id\": \"rb_278zt5u\", \"name\": \"L72_Elite_Kraken\", \"id\": \"t2_16r83m\"}], \"after\": null, \"before\": null}}"))) -------------------------------------------------------------------------------- /test/cassettes/wiki_pages.sexp: -------------------------------------------------------------------------------- 1 | ((request(Post_form(uri https://www.reddit.com/api/v1/access_token)(headers((authorization )))(params((grant_type(password))(username())(password())))))(response(((encoding(Fixed 114))(headers((accept-ranges bytes)(cache-control"max-age=0, must-revalidate")(connection keep-alive)(content-length 114)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 16:15:58 GMT")(server snooserv)(set-cookie"edgebucket=nvGtwCuR4FzzMBbEgV; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"access_token\": \"\", \"token_type\": \"bearer\", \"expires_in\": 3600, \"scope\": \"*\"}")))((request(Get(uri https://oauth.reddit.com/r/ThirdRealm/wiki/pages?raw_json=1)(headers((authorization"bearer ")))))(response(((encoding(Fixed 223))(headers((accept-ranges bytes)(cache-control"private, s-maxage=0, max-age=0, must-revalidate, no-store, max-age=0, must-revalidate")(connection keep-alive)(content-length 223)(content-type"application/json; charset=UTF-8")(date"Sun, 25 Oct 2020 16:15:58 GMT")(expires -1)(server snooserv)(set-cookie"session_tracker=hmpqiflaikolfmdfoi.0.1603642558550.Z0FBQUFBQmZsYVMtZ2ZDRVdkT1BDTlVTcW1HVFAyZVlIZVFYQ0JsUXh1YjlVVXk0aTkwNzFaSEltWkZZaWpnU05RY2p3Si1iOEhuQ01kN3A3VU9qRHdpdzBHZGIwZ2EwQ1JaV0hNbXNRLTBKTXlySWNqQkJUbVNNMFpkN2paQllTQm15Q3hyMnVwR3M; Domain=reddit.com; Max-Age=7199; Path=/; expires=Sun, 25-Oct-2020 18:15:58 GMT; secure; SameSite=None; Secure")(set-cookie"redesign_optout=true; Domain=reddit.com; Max-Age=94607999; Path=/; expires=Wed, 25-Oct-2023 16:15:58 GMT; secure")(set-cookie"csv=1; Max-Age=63072000; Domain=.reddit.com; Path=/; Secure; SameSite=None")(set-cookie"edgebucket=sT8tCSS3UQH01qoMxZ; Domain=reddit.com; Max-Age=63071999; Path=/; secure")(strict-transport-security"max-age=15552000; includeSubDomains; preload")(via"1.1 varnish")(x-content-type-options nosniff)(x-frame-options SAMEORIGIN)(x-moose majestic)(x-ratelimit-remaining 599.0)(x-ratelimit-reset 242)(x-ratelimit-used 1)(x-ua-compatible IE=edge)(x-xss-protection"1; mode=block")))(version HTTP_1_1)(status OK)(flush false))"{\"kind\": \"wikipagelisting\", \"data\": [\"config/automoderator\", \"config/description\", \"config/sidebar\", \"config/stylesheet\", \"config/submit_text\", \"index\", \"praw_test_page\", \"tbsettings\", \"toolbox\", \"usernotes\", \"wiki/index\"]}"))) -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name reddit_api_test) 3 | (libraries core expect_test_helpers_async reddit_api_async) 4 | (inline_tests 5 | (deps 6 | (source_tree cassettes))) 7 | (preprocess 8 | (pps ppx_jane))) 9 | -------------------------------------------------------------------------------- /test/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | include Reddit_api_async 4 | module Time_ns = Time_ns_unix 5 | 6 | let () = 7 | Time_ns_unix.set_sexp_zone Time_ns.Zone.utc; 8 | Log.set_level Reddit_api_async.Logging.log `Debug; 9 | Log.set_output 10 | Reddit_api_async.Logging.log 11 | [ Log.For_testing.create_output ~map_output:Fn.id ] 12 | ;; 13 | 14 | let with_cassette cassette_name ~f = 15 | let credentials = 16 | match Sys.getenv "CREDENTIALS" with 17 | | Some credential_path -> 18 | Sexp.load_sexp_conv_exn credential_path [%of_sexp: Connection.Credentials.t] 19 | | None -> 20 | Password 21 | { username = "TEST_USERNAME" 22 | ; password = "TEST_PASSWORD" 23 | ; client_id = "TEST_CLIENT_ID" 24 | ; client_secret = "TEST_CLIENT_SECRET" 25 | } 26 | in 27 | let filename = "cassettes" ^/ sprintf "%s.sexp" cassette_name in 28 | Connection.For_testing.with_cassette filename ~credentials ~f 29 | ;; 30 | 31 | let get_link_exn connection id = 32 | let%bind link = 33 | Connection.call_exn 34 | connection 35 | (Endpoint.info (Id [ `Link (Thing.Link.Id.of_string id) ])) 36 | >>| List.hd_exn 37 | in 38 | return 39 | (match link with 40 | | `Link link -> link 41 | | _ -> raise_s [%message "Unexpected response item"]) 42 | ;; 43 | -------------------------------------------------------------------------------- /test/test_account.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "friends" = 6 | with_cassette "friends" ~f:(fun connection -> 7 | let%bind body = Connection.call_exn connection (Endpoint.friends ()) in 8 | print_s [%sexp (body : User_list.t)]; 9 | [%expect 10 | {| 11 | (((date (Number 1598656681.0)) (id (String t2_1w72)) (name (String spez)) 12 | (rel_id (String r9_1voxr1)))) |}]; 13 | return ()) 14 | ;; 15 | 16 | let%expect_test "blocked" = 17 | with_cassette "blocked" ~f:(fun connection -> 18 | let%bind body = Connection.call_exn connection (Endpoint.blocked ()) in 19 | print_s [%sexp (body : User_list.t)]; 20 | [%expect 21 | {| 22 | (((date (Number 1598788910.0)) (id (String t2_nn0q)) 23 | (name (String ketralnis)) (rel_id (String r9_1vt16j)))) |}]; 24 | return ()) 25 | ;; 26 | 27 | let%expect_test "messaging" = 28 | with_cassette "messaging" ~f:(fun connection -> 29 | let%bind body = Connection.call_exn connection (Endpoint.messaging ()) in 30 | print_s [%sexp (body : User_list.t)]; 31 | [%expect 32 | {| 33 | (((date (Number 1598789198.0)) (id (String t2_1w72)) (name (String spez)) 34 | (rel_id (String r9_1vt1em)))) |}]; 35 | return ()) 36 | ;; 37 | 38 | let%expect_test "trusted" = 39 | with_cassette "trusted" ~f:(fun connection -> 40 | let%bind body = Connection.call_exn connection (Endpoint.trusted ()) in 41 | print_s [%sexp (body : User_list.t)]; 42 | [%expect 43 | {| 44 | (((date (Number 1598789198.0)) (id (String t2_1w72)) (name (String spez)) 45 | (rel_id (String r9_1vt1em)))) |}]; 46 | return ()) 47 | ;; 48 | -------------------------------------------------------------------------------- /test/test_api.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "me" = 6 | with_cassette "me" ~f:(fun connection -> 7 | let%bind me = Connection.call_exn connection Endpoint.me in 8 | let id = Thing.User.id me in 9 | print_s [%sexp (id : Thing.User.Id.t)]; 10 | [%expect {| 16r83m |}]; 11 | return ()) 12 | ;; 13 | -------------------------------------------------------------------------------- /test/test_comment_fields.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "comment_fields" = 6 | with_cassette "comment_fields" ~f:(fun connection -> 7 | let link = Thing.Link.Id.of_string "hle3h4" in 8 | let%bind ({ comment_forest; _ } : Comment_response.t) = 9 | Connection.call_exn connection (Endpoint.comments () ~link) 10 | in 11 | let first_comment = 12 | match List.hd_exn comment_forest with 13 | | `Comment comment -> comment 14 | | _ -> assert false 15 | in 16 | let keys_from_comment_page = Thing.Comment.field_map first_comment |> Map.key_set in 17 | let comment_id = Thing.Comment.id first_comment in 18 | let%bind keys_from_info_page = 19 | match%bind 20 | Connection.call_exn connection (Endpoint.info (Id [ `Comment comment_id ])) 21 | >>| List.hd_exn 22 | with 23 | | `Comment comment -> return (Thing.Comment.field_map comment |> Map.key_set) 24 | | _ -> assert false 25 | in 26 | let diff = 27 | Set.symmetric_diff keys_from_comment_page keys_from_info_page |> Sequence.to_list 28 | in 29 | print_s [%sexp (diff : (string, string) Either.t list)]; 30 | [%expect {| 31 | ((First depth)) |}]; 32 | print_s [%sexp (Thing.Comment.depth first_comment : int option)]; 33 | [%expect {| (0) |}]; 34 | print_s 35 | [%sexp 36 | (Thing.Comment.body first_comment |> String.split ~on:'\n' |> List.hd_exn 37 | : string)]; 38 | [%expect 39 | {| "- Textual/HTML pretty printing: https://github.com/c-cube/printbox/" |}]; 40 | print_s [%sexp (Thing.Comment.score first_comment : Thing.Comment.Score.t)]; 41 | [%expect {| (Score 11) |}]; 42 | print_s [%sexp (Thing.Comment.link first_comment : Thing.Link.Id.t)]; 43 | [%expect {| hle3h4 |}]; 44 | print_s [%sexp (Thing.Comment.permalink first_comment |> Uri.to_string : string)]; 45 | [%expect 46 | {| https://reddit.com/r/ocaml/comments/hle3h4/ocaml_is_superbly_suited_to_defining_and/fwzc1p0/ |}]; 47 | print_s [%sexp (keys_from_info_page : Set.M(String).t)]; 48 | [%expect 49 | {| 50 | (all_awardings approved_at_utc approved_by archived associated_award author 51 | author_flair_background_color author_flair_css_class author_flair_richtext 52 | author_flair_template_id author_flair_text author_flair_text_color 53 | author_flair_type author_fullname author_patreon_flair author_premium 54 | awarders banned_at_utc banned_by body body_html can_gild can_mod_post 55 | collapsed collapsed_because_crowd_control collapsed_reason controversiality 56 | created created_utc distinguished downs edited gilded gildings id 57 | is_submitter likes link_id locked mod_note mod_reason_by mod_reason_title 58 | mod_reports name no_follow num_reports parent_id permalink removal_reason 59 | replies report_reasons saved score score_hidden send_replies stickied 60 | subreddit subreddit_id subreddit_name_prefixed subreddit_type 61 | top_awarded_type total_awards_received treatment_tags ups user_reports) |}]; 62 | return ()) 63 | ;; 64 | -------------------------------------------------------------------------------- /test/test_delete.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "delete" = 6 | with_cassette "delete" ~f:(fun connection -> 7 | let id = `Comment (Thing.Comment.Id.of_string "g3f4icy") in 8 | let%bind () = Connection.call_exn connection (Endpoint.delete ~id) in 9 | [%expect]; 10 | return ()) 11 | ;; 12 | -------------------------------------------------------------------------------- /test/test_error_handling.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "Json error" = 6 | let ban_message = 7 | List.init 8 | 15 9 | ~f: 10 | (const 11 | "This is a ban message. Because it is so long, it will be rejected by Reddit. ") 12 | |> String.concat 13 | in 14 | print_s [%sexp (String.length ban_message : int)]; 15 | [%expect {| 1155 |}]; 16 | with_cassette "ban__long_message" ~f:(fun connection -> 17 | let%bind () = 18 | match%bind 19 | Connection.call 20 | connection 21 | (Endpoint.add_relationship 22 | ~relationship:Banned 23 | ~username:(Username.of_string "spez") 24 | ~duration:Permanent 25 | ~subreddit:(Subreddit_name.of_string "thirdrealm") 26 | ~ban_message 27 | ()) 28 | with 29 | | Ok () -> raise_s [%message "Expected API error"] 30 | | Error error -> 31 | print_s [%message "" (error : Endpoint.Error.t Connection.Error.t)]; 32 | return () 33 | in 34 | [%expect 35 | {| 36 | (error 37 | (Endpoint_error 38 | (Json_response_errors 39 | (((error TOO_LONG) (error_type ()) 40 | (details "this is too long (max: 1000)") (fields (ban_message))))))) |}]; 41 | return ()) 42 | ;; 43 | 44 | let%expect_test "HTTP error" = 45 | with_cassette "ban__wrong_subreddit" ~f:(fun connection -> 46 | let%bind () = 47 | match%bind 48 | Connection.call 49 | connection 50 | (Endpoint.add_relationship 51 | ~relationship:Banned 52 | ~username:(Username.of_string "spez") 53 | ~duration:Permanent 54 | ~subreddit:(Subreddit_name.of_string "thirdrealm") 55 | ()) 56 | with 57 | | Error (Endpoint_error (Http_error _)) -> 58 | print_s [%message "HTTP error"]; 59 | return () 60 | | Ok () | Error _ -> raise_s [%message "Expected HTTP error"] 61 | in 62 | [%expect {| "HTTP error" |}]; 63 | return ()) 64 | ;; 65 | 66 | let%expect_test "Bad request" = 67 | with_cassette "user_trophies__nonexistent_user" ~f:(fun connection -> 68 | let%bind () = 69 | match%bind 70 | Connection.call 71 | connection 72 | (Endpoint.user_trophies 73 | ~username:(Username.of_string "thisusershouldnotexist")) 74 | with 75 | | Ok _ -> raise_s [%message "Expected error"] 76 | | Error error -> 77 | print_s [%message "" (error : Endpoint.Error.t Connection.Error.t)]; 78 | return () 79 | in 80 | [%expect 81 | {| 82 | (error 83 | (Endpoint_error 84 | (Json_response_errors 85 | (((error USER_DOESNT_EXIST) (error_type ()) 86 | (details "that user doesn't exist") (fields (id))))))) |}]; 87 | return ()) 88 | ;; 89 | -------------------------------------------------------------------------------- /test/test_hot.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "hot" = 6 | with_cassette "hot" ~f:(fun connection -> 7 | let%bind link = 8 | Connection.call_exn 9 | connection 10 | (Endpoint.hot ~limit:1 ~subreddit:(Subreddit_name.of_string "ThirdRealm") ()) 11 | >>| Listing.children 12 | >>| List.hd_exn 13 | in 14 | print_s 15 | [%sexp 16 | { id : Thing.Link.Id.t = Thing.Link.id link 17 | ; title : string = Thing.Link.title link 18 | ; author : Username.t option = Thing.Link.author link 19 | ; creation_time : Time_ns.t = Thing.Link.creation_time link 20 | ; is_stickied : bool = Thing.Link.is_stickied link 21 | }]; 22 | [%expect 23 | {| 24 | ((id fa5dg9) 25 | (title "/r/thirdrealm Open Discussion Thread | February 26, 2020") 26 | (author (BernardJOrtcutt)) (creation_time (2020-02-27 02:55:31.000000000Z)) 27 | (is_stickied true)) |}]; 28 | return ()) 29 | ;; 30 | 31 | let%expect_test "hot__multiple_subreddits" = 32 | with_cassette "hot__multiple_subreddits" ~f:(fun connection -> 33 | let subreddit = 34 | List.map [ "aww"; "programming" ] ~f:Subreddit_name.of_string 35 | |> Subreddit_name.combine 36 | in 37 | let%bind links = 38 | Connection.call_exn connection (Endpoint.hot ~limit:10 ~subreddit ()) 39 | >>| Listing.children 40 | in 41 | List.iter links ~f:(fun link -> 42 | print_s 43 | [%sexp 44 | { subreddit : Subreddit_name.t = Thing.Link.subreddit link 45 | ; id : Thing.Link.Id.t = Thing.Link.id link 46 | }]); 47 | [%expect 48 | {| 49 | ((subreddit aww) (id j0q3oj)) 50 | ((subreddit programming) (id j0oriz)) 51 | ((subreddit aww) (id j0omwl)) 52 | ((subreddit programming) (id j06gd7)) 53 | ((subreddit aww) (id j0mb8j)) 54 | ((subreddit aww) (id j0n001)) 55 | ((subreddit aww) (id j0nuzp)) 56 | ((subreddit aww) (id j0og2d)) 57 | ((subreddit aww) (id j0li9w)) 58 | ((subreddit aww) (id j0njcb)) |}]; 59 | return ()) 60 | ;; 61 | -------------------------------------------------------------------------------- /test/test_id36.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "roundtrip: int -> string -> int" = 6 | Quickcheck.test 7 | (Int63.gen_incl Int63.zero Int63.max_value) 8 | ~f:(fun n -> assert (Int63.equal (Id36.to_int63 (Id36.of_int63 n)) n)) 9 | ~examples: 10 | (List.map 11 | [ 85562; 18527; 61915; 6302; 60590; 2723; 97946; 78956; 48802; 84286 ] 12 | ~f:Int63.of_int) 13 | ~sexp_of:[%sexp_of: Int63.t]; 14 | return () 15 | ;; 16 | 17 | let%expect_test "roundtrip: string -> int -> string" = 18 | let rec drop_unnecessary_zeroes_from_string string = 19 | match string with 20 | | "0" -> string 21 | | _ -> 22 | (match string.[0] with 23 | | '0' -> drop_unnecessary_zeroes_from_string (String.drop_prefix string 1) 24 | | _ -> string) 25 | in 26 | Quickcheck.test 27 | (Int.gen_incl 1 11 28 | |> Quickcheck.Generator.bind ~f:(fun length -> 29 | String.gen_with_length 30 | length 31 | (Quickcheck.Generator.union [ Char.gen_lowercase; Char.gen_digit ]))) 32 | ~f:(fun string -> 33 | assert ( 34 | String.equal 35 | (Id36.to_string (Id36.of_string string)) 36 | (drop_unnecessary_zeroes_from_string string))) 37 | ~examples: 38 | [ "0" 39 | ; "10" 40 | ; "a1" 41 | ; "aklzj" 42 | ; "1o6fl" 43 | ; "wh6sj" 44 | ; "kldtp" 45 | ; "mmax3" 46 | ; "hqa43" 47 | ; "2p0qz" 48 | ; "nikcv" 49 | ; "8a93d" 50 | ] 51 | ~sexp_of:[%sexp_of: string]; 52 | return () 53 | ;; 54 | 55 | let%expect_test "prefixes" = 56 | List.iter [ "0"; "a1"; "aklzj"; "t1_0"; "t1_a1"; "t1_aklzj" ] ~f:(fun test_case -> 57 | Thing.Comment.Id.of_string test_case |> [%sexp_of: Thing.Comment.Id.t] |> print_s); 58 | [%expect {| 59 | 0 60 | a1 61 | aklzj 62 | 0 63 | a1 64 | aklzj |}]; 65 | return () 66 | ;; 67 | -------------------------------------------------------------------------------- /test/test_info.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "info" = 6 | with_cassette "info" ~f:(fun connection -> 7 | let%bind link = 8 | Connection.call_exn 9 | connection 10 | (Endpoint.info (Id [ `Link (Thing.Link.Id.of_string "hmjd8r") ])) 11 | >>| List.hd_exn 12 | in 13 | let link = 14 | match link with 15 | | `Link link -> link 16 | | _ -> raise_s [%message "Unexpected response item"] 17 | in 18 | print_s 19 | [%sexp 20 | { id : Thing.Link.Id.t = Thing.Link.id link 21 | ; title : string = Thing.Link.title link 22 | ; author : Username.t option = Thing.Link.author link 23 | ; creation_time : Time_ns.t = Thing.Link.creation_time link 24 | ; is_stickied : bool = Thing.Link.is_stickied link 25 | }]; 26 | [%expect 27 | {| 28 | ((id hmjd8r) (title "This is a title") (author (BJO_test_user)) 29 | (creation_time (2020-07-06 23:42:12.000000000Z)) (is_stickied false)) |}]; 30 | return ()) 31 | ;; 32 | 33 | let%expect_test "info__by_subreddit_name" = 34 | with_cassette "info__by_subreddit_name" ~f:(fun connection -> 35 | let%bind subreddits = 36 | Connection.call_exn 37 | connection 38 | (Endpoint.info 39 | (Subreddit_name 40 | (List.map [ "ocaml"; "redditdev"; "python" ] ~f:Subreddit_name.of_string))) 41 | >>| List.map ~f:(function 42 | | `Subreddit subreddit -> subreddit 43 | | _ -> raise_s [%message "Unexpected response item"]) 44 | in 45 | print_s 46 | [%sexp (List.map subreddits ~f:Thing.Subreddit.name : Subreddit_name.t list)]; 47 | [%expect {| (redditdev ocaml Python) |}]; 48 | return ()) 49 | ;; 50 | -------------------------------------------------------------------------------- /test/test_link_fields.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "link_fields__url_contents" = 6 | with_cassette "link_fields__url_contents" ~f:(fun connection -> 7 | let link = Thing.Link.Id.of_string "lfx8ac" in 8 | let%bind ({ link; _ } : Comment_response.t) = 9 | Connection.call_exn connection (Endpoint.comments () ~link) 10 | in 11 | let contents = Thing.Link.contents link in 12 | print_s [%sexp (contents : Thing.Link.Contents.t)]; 13 | [%expect {| (Url https://i.redd.it/ap95zxoqleg61.jpg) |}]; 14 | return ()) 15 | ;; 16 | 17 | let%expect_test "link_fields__self_contents" = 18 | with_cassette "link_fields__self_contents" ~f:(fun connection -> 19 | let link = Thing.Link.Id.of_string "kvzaot" in 20 | let%bind ({ link; _ } : Comment_response.t) = 21 | Connection.call_exn connection (Endpoint.comments () ~link) 22 | in 23 | let contents : Thing.Link.Contents.t = 24 | match Thing.Link.contents link with 25 | | Self_text text -> Self_text (String.prefix text 100) 26 | | v -> v 27 | in 28 | print_s [%sexp (contents : Thing.Link.Contents.t)]; 29 | [%expect 30 | {| 31 | (Self_text 32 | "As part of modernizing our OAuth2 infrastructure, we\226\128\153re implementing some potentially breaking cha") |}]; 33 | return ()) 34 | ;; 35 | -------------------------------------------------------------------------------- /test/test_links_and_comments.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "edit" = 6 | with_cassette "edit" ~f:(fun connection -> 7 | let id = `Comment (Thing.Comment.Id.of_string "g3krlj5") in 8 | let%bind comment = 9 | Connection.call_exn connection (Endpoint.edit () ~id ~text:"edited text") 10 | in 11 | print_s [%sexp (Thing.Poly.fullname comment : Thing.Fullname.t)]; 12 | [%expect {| (Comment g3krlj5) |}]; 13 | return ()) 14 | ;; 15 | 16 | let%expect_test "save" = 17 | with_cassette "save" ~f:(fun connection -> 18 | let id = `Comment (Thing.Comment.Id.of_string "g3krlj5") in 19 | let%bind () = Connection.call_exn connection (Endpoint.save () ~id) in 20 | [%expect {| |}]; 21 | return ()) 22 | ;; 23 | 24 | let%expect_test "unsave" = 25 | with_cassette "unsave" ~f:(fun connection -> 26 | let id = `Comment (Thing.Comment.Id.of_string "g3krlj5") in 27 | let%bind () = Connection.call_exn connection (Endpoint.unsave ~id) in 28 | (* Unsave is idempotent *) 29 | let%bind () = Connection.call_exn connection (Endpoint.unsave ~id) in 30 | [%expect {| |}]; 31 | return ()) 32 | ;; 33 | 34 | let%expect_test "send_replies" = 35 | with_cassette "send_replies" ~f:(fun connection -> 36 | let id = `Comment (Thing.Comment.Id.of_string "g3krlj5") in 37 | let%bind () = 38 | Connection.call_exn connection (Endpoint.send_replies ~id ~enabled:true) 39 | in 40 | [%expect]; 41 | let%bind () = 42 | Connection.call_exn connection (Endpoint.send_replies ~id ~enabled:false) 43 | in 44 | [%expect {| |}]; 45 | return ()) 46 | ;; 47 | 48 | let%expect_test "set_contest_mode" = 49 | with_cassette "set_contest_mode" ~f:(fun connection -> 50 | let link = Thing.Link.Id.of_string "hofd3k" in 51 | let%bind () = 52 | Connection.call_exn connection (Endpoint.set_contest_mode ~link ~enabled:true) 53 | in 54 | let%bind () = 55 | Connection.call_exn connection (Endpoint.set_contest_mode ~link ~enabled:false) 56 | in 57 | [%expect {| |}]; 58 | return ()) 59 | ;; 60 | 61 | let%expect_test "spoiler" = 62 | with_cassette "spoiler" ~f:(fun connection -> 63 | let link = Thing.Link.Id.of_string "hofd3k" in 64 | let%bind () = Connection.call_exn connection (Endpoint.spoiler ~link) in 65 | [%expect]; 66 | return ()) 67 | ;; 68 | 69 | let%expect_test "unspoiler" = 70 | with_cassette "unspoiler" ~f:(fun connection -> 71 | let link = Thing.Link.Id.of_string "hofd3k" in 72 | let%bind () = Connection.call_exn connection (Endpoint.unspoiler ~link) in 73 | [%expect]; 74 | return ()) 75 | ;; 76 | 77 | let%expect_test "vote" = 78 | with_cassette "vote" ~f:(fun connection -> 79 | let target = `Comment (Thing.Comment.Id.of_string "g3krlj5") in 80 | let%bind () = 81 | Connection.call_exn connection (Endpoint.vote () ~target ~direction:Down) 82 | in 83 | let%bind () = 84 | Connection.call_exn connection (Endpoint.vote () ~target ~direction:Neutral) 85 | in 86 | let%bind () = 87 | Connection.call_exn connection (Endpoint.vote () ~target ~direction:Up) 88 | in 89 | [%expect {| |}]; 90 | return ()) 91 | ;; 92 | -------------------------------------------------------------------------------- /test/test_listings.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "best" = 6 | with_cassette "best" ~f:(fun connection -> 7 | let%bind listing = Connection.call_exn connection (Endpoint.best ()) in 8 | let child_ids = Listing.children listing |> List.map ~f:Thing.Link.id in 9 | print_s [%sexp (child_ids : Thing.Link.Id.t list)]; 10 | [%expect 11 | {| 12 | (ikj8uc ikuyq6 iksp32 ikpbi4 ikveil ikdwtu ikg3cx ikksvt ikrful iki288 ikpyqv 13 | ikl4g6 iknntd ikrrxo ikl05w ikn7gl iksu0i ikqhgj ikp413 ikpayi ikkxfh ikkwlk 14 | ikoad9 ikudf8 ikmxz9) |}]; 15 | return ()) 16 | ;; 17 | 18 | let%expect_test "links_by_id" = 19 | with_cassette "links_by_id" ~f:(fun connection -> 20 | let links = List.map ~f:Thing.Link.Id.of_string [ "icqrut"; "ikksvt" ] in 21 | let%bind listing = Connection.call_exn connection (Endpoint.links_by_id ~links) in 22 | let ids = Listing.children listing |> List.map ~f:Thing.Link.id in 23 | print_s [%sexp (ids : Thing.Link.Id.t list)]; 24 | [%expect {| (icqrut ikksvt) |}]; 25 | return ()) 26 | ;; 27 | 28 | let%expect_test "random" = 29 | with_cassette "random" ~f:(fun connection -> 30 | let%bind link_id = 31 | Connection.call_exn 32 | connection 33 | (Endpoint.random ~subreddit:(Subreddit_name.of_string "ocaml") ()) 34 | in 35 | print_s [%sexp (link_id : Thing.Link.Id.t)]; 36 | [%expect {| feyhbv |}]; 37 | return ()) 38 | ;; 39 | 40 | let%expect_test "user_overview" = 41 | with_cassette "user_overview" ~f:(fun connection -> 42 | let%bind listing = 43 | Connection.call_exn 44 | connection 45 | (Endpoint.user_overview () ~username:(Username.of_string "spez")) 46 | in 47 | let child_fullnames = Listing.children listing |> List.map ~f:Thing.Poly.fullname in 48 | print_s [%sexp (child_fullnames : Thing.Fullname.t list)]; 49 | [%expect 50 | {| 51 | ((Comment hroelsq) (Comment hrdseug) (Comment hgsd3e4) (Comment hf0kd4r) 52 | (Comment hbhuafj) (Comment hbhpx8f) (Comment hbhmscu) (Link pbmy5y) 53 | (Comment h0xuw5y) (Comment gzof6q0) (Comment gvyvlig) (Link mcisdf) 54 | (Comment grivhb5) (Comment go1nkrg) (Comment go1ixbd) (Comment gmj9aya) 55 | (Comment glroko6) (Comment gl07p47) (Comment ggbcn1t) (Comment gf2hmo6) 56 | (Comment gdlk75z) (Comment gbjoobr) (Comment gbdoob3) (Comment ga80wbq) 57 | (Comment g9u720a)) |}]; 58 | return ()) 59 | ;; 60 | -------------------------------------------------------------------------------- /test/test_modmail.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "create_modmail_conversation" = 6 | with_cassette "create_modmail_conversation" ~f:(fun connection -> 7 | let%bind conversation = 8 | Connection.call_exn 9 | connection 10 | (Endpoint.create_modmail_conversation 11 | ~subject:"Test subject" 12 | ~body:"Test body" 13 | ~subreddit:(Subreddit_name.of_string "ThirdRealm") 14 | ~to_:(User (Username.of_string "BJO_test_user")) 15 | ~hide_author:false) 16 | in 17 | print_s [%sexp (conversation : Modmail.Conversation.t)]; 18 | [%expect 19 | {| 20 | ((conversation 21 | (Object 22 | ((isAuto False) 23 | (objIds (Array ((Object ((id (String osvgj)) (key (String messages))))))) 24 | (isRepliable True) (lastUserUpdate Null) (isInternal False) 25 | (lastModUpdate (String 2020-07-26T21:18:43.146061+00:00)) 26 | (lastUpdated (String 2020-07-26T21:18:43.146061+00:00)) 27 | (authors 28 | (Array 29 | ((Object 30 | ((isMod True) (isAdmin False) (name (String L72_Elite_Kraken)) 31 | (isOp True) (isParticipant False) (isHidden False) 32 | (id (Number 71814082)) (isDeleted False)))))) 33 | (owner 34 | (Object 35 | ((displayName (String ThirdRealm)) (type (String subreddit)) 36 | (id (String t5_390u2))))) 37 | (id (String fsv44)) (isHighlighted False) 38 | (subject (String "Test subject")) (participant (Object ())) 39 | (state (Number 0)) (lastUnread Null) (numMessages (Number 1))))) 40 | (messages 41 | (Object 42 | ((osvgj 43 | (Object 44 | ((body 45 | (String 46 | "

Test body

\ 47 | \n
")) 48 | (author 49 | (Object 50 | ((isMod True) (isAdmin False) (name (String L72_Elite_Kraken)) 51 | (isOp True) (isParticipant False) (isHidden False) 52 | (id (Number 71814082)) (isDeleted False)))) 53 | (isInternal False) (date (String 2020-07-26T21:18:43.146061+00:00)) 54 | (bodyMarkdown (String "Test body")) (id (String osvgj)))))))) 55 | (modActions (Object ()))) |}]; 56 | return ()) 57 | ;; 58 | 59 | let%expect_test "reply_modmail_conversation" = 60 | with_cassette "reply_modmail_conversation" ~f:(fun connection -> 61 | let%bind conversation = 62 | Connection.call_exn 63 | connection 64 | (Endpoint.reply_modmail_conversation 65 | ~conversation_id:(Modmail.Conversation.Id.of_string "fsv44") 66 | ~body:"Message body" 67 | ~hide_author:false 68 | ~internal:false) 69 | in 70 | print_s 71 | [%sexp 72 | { conversation_id : Modmail.Conversation.Id.t = 73 | Modmail.Conversation.id conversation 74 | }]; 75 | [%expect {| ((conversation_id fsv44)) |}]; 76 | return ()) 77 | ;; 78 | -------------------------------------------------------------------------------- /test/test_oauth2_userless.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let with_cassette cassette_name ~f ~is_confidential = 6 | let credentials = 7 | match Sys.getenv "CREDENTIALS_USERLESS" with 8 | | Some credential_path -> 9 | Sexp.load_sexp_conv_exn credential_path [%of_sexp: Connection.Credentials.t] 10 | | None -> 11 | (match is_confidential with 12 | | true -> 13 | Userless_confidential 14 | { client_id = "TEST_CLIENT_ID"; client_secret = "TEST_CLIENT_SECRET" } 15 | | false -> 16 | Userless_public 17 | { client_id = "TEST_CLIENT_ID"; device_id = Some "TEST_DEVICE_ID" }) 18 | in 19 | let filename = "cassettes" ^/ sprintf "%s.sexp" cassette_name in 20 | Connection.For_testing.with_cassette filename ~credentials ~f 21 | ;; 22 | 23 | let%expect_test "userless_confidential" = 24 | with_cassette 25 | "userless_confidential" 26 | ~f:(fun connection -> 27 | let%bind link = get_link_exn connection "odlsl2" in 28 | print_s 29 | [%sexp 30 | { id : Thing.Link.Id.t = Thing.Link.id link 31 | ; title : string = Thing.Link.title link 32 | }]; 33 | [%expect {| ((id odlsl2) (title test)) |}]; 34 | return ()) 35 | ~is_confidential:true 36 | ;; 37 | 38 | let%expect_test "userless_public" = 39 | with_cassette 40 | "userless_public" 41 | ~f:(fun connection -> 42 | let%bind link = get_link_exn connection "odlsl2" in 43 | print_s 44 | [%sexp 45 | { id : Thing.Link.Id.t = Thing.Link.id link 46 | ; title : string = Thing.Link.title link 47 | }]; 48 | [%expect {| ((id odlsl2) (title test)) |}]; 49 | return ()) 50 | ~is_confidential:false 51 | ;; 52 | -------------------------------------------------------------------------------- /test/test_report.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "report" = 6 | with_cassette "report" ~f:(fun connection -> 7 | let target = `Link (Thing.Link.Id.of_string "hony5b") in 8 | let%bind () = 9 | Connection.call_exn connection (Endpoint.report () ~target ~reason:"Test report") 10 | in 11 | [%expect {| |}]; 12 | return ()) 13 | ;; 14 | -------------------------------------------------------------------------------- /test/test_select_flair.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "select_flair" = 6 | with_cassette "select_flair" ~f:(fun connection -> 7 | let subreddit = Subreddit_name.of_string "ThirdRealm" in 8 | let link = Thing.Link.Id.of_string "hmjghn" in 9 | let flair_template_id = Uuid.of_string "6c5ea4bc-c16c-11ea-9a01-0ea60516144b" in 10 | let%bind () = 11 | Connection.call_exn 12 | connection 13 | (Endpoint.select_flair ~flair_template_id () ~subreddit ~target:(Link link)) 14 | in 15 | [%expect {| |}]; 16 | return ()) 17 | ;; 18 | -------------------------------------------------------------------------------- /test/test_set_subreddit_sticky.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "set_subreddit_sticky" = 6 | with_cassette "set_subreddit_sticky" ~f:(fun connection -> 7 | let link = Thing.Link.Id.of_string "f7vspj" in 8 | let%bind () = 9 | Connection.call_exn 10 | connection 11 | (Endpoint.set_subreddit_sticky 12 | () 13 | ~link 14 | ~sticky_state:(Sticky { slot = Some 2 })) 15 | in 16 | [%expect]; 17 | let%bind () = 18 | Connection.call_exn 19 | connection 20 | (Endpoint.set_subreddit_sticky () ~link ~sticky_state:Unsticky) 21 | in 22 | [%expect {| |}]; 23 | return ()) 24 | ;; 25 | -------------------------------------------------------------------------------- /test/test_set_suggested_sort.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "set_suggested_sort" = 6 | with_cassette "set_suggested_sort" ~f:(fun connection -> 7 | let link = Thing.Link.Id.of_string "hmjghn" in 8 | let%bind () = 9 | Connection.call_exn 10 | connection 11 | (Endpoint.set_suggested_sort ~sort:(Some New) ~link) 12 | in 13 | [%expect {| |}]; 14 | return ()) 15 | ;; 16 | -------------------------------------------------------------------------------- /test/test_submit.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "submit" = 6 | with_cassette "submit" ~f:(fun connection -> 7 | let title = "Test post title" in 8 | let subreddit = Subreddit_name.of_string "ThirdRealm" in 9 | let%bind id, uri = 10 | Connection.call_exn 11 | connection 12 | (Endpoint.submit 13 | () 14 | ~title 15 | ~subreddit 16 | ~kind:(Self (Markdown "This is a post body."))) 17 | in 18 | print_s 19 | [%message 20 | "Submission attributes" (id : Thing.Link.Id.t) ~uri:(Uri.to_string uri : string)]; 21 | [%expect 22 | {| 23 | ("Submission attributes" (id hmjghn) 24 | (uri https://www.reddit.com/r/ThirdRealm/comments/hmjghn/test_post_title/)) |}]; 25 | return ()) 26 | ;; 27 | 28 | let%expect_test "submit__crosspost" = 29 | with_cassette "submit__crosspost" ~f:(fun connection -> 30 | let title = "Crosspost" in 31 | let subreddit = Subreddit_name.of_string "ThirdRealm" in 32 | let%bind id, uri = 33 | Connection.call_exn 34 | connection 35 | (Endpoint.submit 36 | () 37 | ~title 38 | ~subreddit 39 | ~kind:(Crosspost (Thing.Link.Id.of_string "box80e"))) 40 | in 41 | print_s 42 | [%message 43 | "Submission attributes" (id : Thing.Link.Id.t) ~uri:(Uri.to_string uri : string)]; 44 | [%expect 45 | {| 46 | ("Submission attributes" (id ili4vc) 47 | (uri https://www.reddit.com/r/ThirdRealm/comments/ili4vc/crosspost/)) |}]; 48 | return ()) 49 | ;; 50 | -------------------------------------------------------------------------------- /test/test_subreddit_fields.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "subreddit_fields" = 6 | with_cassette "subreddit_fields" ~f:(fun connection -> 7 | let%bind subreddit = 8 | Connection.call_exn 9 | connection 10 | (Endpoint.about_subreddit ~subreddit:(Subreddit_name.of_string "ocaml")) 11 | in 12 | print_s [%sexp (Thing.Subreddit.name subreddit : Subreddit_name.t)]; 13 | [%expect {| ocaml |}]; 14 | print_s [%sexp (Thing.Subreddit.title subreddit : string)]; 15 | [%expect {| "let reddit = OCaml;;" |}]; 16 | print_s [%sexp (String.prefix (Thing.Subreddit.description subreddit) 80 : string)]; 17 | [%expect 18 | {| 19 | "[OCaml](http://ocaml.org/) is a statically typed functional programming language" |}]; 20 | print_s [%sexp (Thing.Subreddit.subscribers subreddit : int)]; 21 | [%expect {| 7554 |}]; 22 | print_s [%sexp (Thing.Subreddit.active_users subreddit : int)]; 23 | [%expect {| 12 |}]; 24 | print_s [%sexp (Thing.Subreddit.creation_time subreddit : Time_ns.t)]; 25 | [%expect {| (2008-01-25 13:24:41.000000000Z) |}]; 26 | return ()) 27 | ;; 28 | -------------------------------------------------------------------------------- /test/test_subreddit_name.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "Normalization" = 6 | let subreddit_names = 7 | List.map 8 | ~f:Subreddit_name.of_string 9 | [ "askphilosophy"; "r/askphilosophy"; "/r/askphilosophy"; "/r/AskPhilosophy" ] 10 | in 11 | let set = Set.of_list (module Subreddit_name) subreddit_names in 12 | print_s [%message "" (set : Set.M(Subreddit_name).t)]; 13 | [%expect {| (set (askphilosophy)) |}]; 14 | let hash_set = Hash_set.of_list (module Subreddit_name) subreddit_names in 15 | print_s [%message "" (hash_set : Hash_set.M(Subreddit_name).t)]; 16 | [%expect {| (hash_set (askphilosophy)) |}]; 17 | return () 18 | ;; 19 | 20 | let%expect_test "User subreddits" = 21 | let username = Username.of_string "spez" in 22 | let subreddit_name = Subreddit_name.user_subreddit username in 23 | print_s [%message "" (subreddit_name : Subreddit_name.t)]; 24 | [%expect {| (subreddit_name u_spez) |}]; 25 | return () 26 | ;; 27 | -------------------------------------------------------------------------------- /test/test_user_fields.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "user_fields" = 6 | with_cassette "user_fields" ~f:(fun connection -> 7 | let%bind user = 8 | Connection.call_exn 9 | connection 10 | (Endpoint.about_user ~username:(Username.of_string "spez")) 11 | in 12 | print_s [%sexp (Thing.User.name user : Username.t)]; 13 | [%expect {| spez |}]; 14 | print_s [%sexp (Thing.User.creation_time user : Time_ns.t)]; 15 | [%expect {| (2005-06-06 04:00:00.000000000Z) |}]; 16 | print_s [%sexp (Thing.User.link_karma user : int)]; 17 | [%expect {| 138988 |}]; 18 | print_s [%sexp (Thing.User.comment_karma user : int)]; 19 | [%expect {| 743899 |}]; 20 | print_s [%sexp (Thing.User.awarder_karma user : int)]; 21 | [%expect {| 625 |}]; 22 | print_s [%sexp (Thing.User.awardee_karma user : int)]; 23 | [%expect {| 62329 |}]; 24 | print_s [%sexp (Thing.User.total_karma user : int)]; 25 | [%expect {| 945841 |}]; 26 | print_s 27 | [%sexp (Thing.User.subreddit user |> Thing.Subreddit.name : Subreddit_name.t)]; 28 | [%expect {| u_spez |}]; 29 | return ()) 30 | ;; 31 | -------------------------------------------------------------------------------- /test/test_username.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "Normalization" = 6 | let usernames = 7 | List.map ~f:Username.of_string [ "spez"; "u/spez"; "/u/spez"; "/u/SPEZ" ] 8 | in 9 | let set = Set.of_list (module Username) usernames in 10 | print_s [%message "" (set : Set.M(Username).t)]; 11 | [%expect {| (set (spez)) |}]; 12 | let hash_set = Hash_set.of_list (module Username) usernames in 13 | print_s [%message "" (hash_set : Hash_set.M(Username).t)]; 14 | [%expect {| (hash_set (spez)) |}]; 15 | return () 16 | ;; 17 | -------------------------------------------------------------------------------- /test/test_users.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Async 3 | open! Import 4 | 5 | let%expect_test "user_upvoted" = 6 | with_cassette "user_upvoted" ~f:(fun connection -> 7 | match%bind 8 | Connection.call_exn 9 | connection 10 | (Endpoint.user_upvoted () ~username:(Username.of_string "spez")) 11 | with 12 | | `Private -> return () 13 | | `Listing _ -> failwith "Got listing unexpectedly") 14 | ;; 15 | --------------------------------------------------------------------------------