# Changelog

## v0.7.0

  * Breaking changes
    - Flatten the facade: every endpoint function that used to live on an
      entity module (`Hunter.Account`, `Hunter.Application`,
      `Hunter.Attachment`, `Hunter.Client`, `Hunter.Context`,
      `Hunter.Instance`, `Hunter.List`, `Hunter.Notification`, `Hunter.Poll`,
      `Hunter.Relationship`, `Hunter.Report`, `Hunter.Result`,
      `Hunter.Status`, `Hunter.WebPushSubscription`) was removed and now
      lives only on `Hunter`, with the exact same name, arity, and default
      arguments — the return value is unchanged. These modules are now pure
      data definitions (`defstruct`, `@type t`, and field docs only); their
      `@type t` structs are otherwise unchanged. `Hunter.Domain` had no
      struct and was deleted entirely; its three functions moved to
      `Hunter` unchanged. Migration is mechanical: replace
      `Hunter.<Entity>.<function>(...)` with `Hunter.<function>(...)` at
      every call site, per the table below. Internally,
      `Hunter.Api.HTTPClient` was deleted; the single transport is now
      `Hunter.Api.Request.request!/6`

      | Old module | Old call → new call |
      |---|---|
      | `Hunter.Account` | `Hunter.Account.followers/3` → `Hunter.followers/3`; also `account/2`, `verify_credentials/1`, `update_credentials/2`, `following/3`, `search_account/2`, `blocks/2`, `follow_requests/2`, `mutes/2`, `accept_follow_request/2`, `reject_follow_request/2`, `reblogged_by/3`, `favourited_by/3` |
      | `Hunter.Application` | `Hunter.Application.create_app/5` → `Hunter.create_app/5`; `Hunter.Application.load_credentials/1` → `Hunter.load_credentials/1` |
      | `Hunter.Attachment` | `Hunter.Attachment.upload_media/3` → `Hunter.upload_media/3`; also `media_attachment/2`, `update_media/3`, `delete_media/2` |
      | `Hunter.Client` | `Hunter.Client.new/1` → `Hunter.new/1`; `Hunter.Client.log_in/4` → `Hunter.log_in/4`; `Hunter.Client.log_in_oauth/3` → `Hunter.log_in_oauth/3`; `Hunter.Client.user_agent/0` → `Hunter.user_agent/0` |
      | `Hunter.Context` | `Hunter.Context.status_context/2` → `Hunter.status_context/2` |
      | `Hunter.Domain` (module deleted) | `Hunter.Domain.blocked_domains/2` → `Hunter.blocked_domains/2`; also `block_domain/2`, `unblock_domain/2` |
      | `Hunter.Instance` | `Hunter.Instance.instance_info/1` → `Hunter.instance_info/1` |
      | `Hunter.List` | `Hunter.List.lists/1` → `Hunter.lists/1`; also `list/2`, `create_list/3`, `update_list/3`, `destroy_list/2`, `list_accounts/3`, `add_accounts_to_list/3`, `remove_accounts_from_list/3`, `account_lists/2` |
      | `Hunter.Notification` | `Hunter.Notification.notifications/2` → `Hunter.notifications/2`; also `notification/2`, `clear_notifications/1`, `clear_notification/2`, `unread_count/1`, `notification_policy/1`, `update_notification_policy/2`, `notification_requests/2`, `notification_request/2`, `accept_notification_request/2`, `dismiss_notification_request/2`, `accept_notification_requests/2`, `dismiss_notification_requests/2`, `notification_requests_merged?/1`, `grouped_notifications/2`, `notification_group/2`, `dismiss_notification_group/2`, `notification_group_accounts/2`, `grouped_unread_count/1` |
      | `Hunter.Poll` | `Hunter.Poll.poll/2` → `Hunter.poll/2`; `Hunter.Poll.vote/3` → `Hunter.vote/3` |
      | `Hunter.Relationship` | `Hunter.Relationship.follow/2` → `Hunter.follow/2`; also `unfollow/2`, `block/2`, `unblock/2`, `mute/2`, `unmute/2`, `relationships/2` |
      | `Hunter.Report` | `Hunter.Report.report/4` → `Hunter.report/4` |
      | `Hunter.Result` | `Hunter.Result.search/3` → `Hunter.search/3` |
      | `Hunter.Status` | `Hunter.Status.create_status/3` → `Hunter.create_status/3`; also `status/2`, `statuses_by_ids/2`, `edit_status/4`, `status_history/2`, `status_source/2`, `bookmark/2`, `unbookmark/2`, `bookmarks/2`, `pin/2`, `unpin/2`, `mute_conversation/2`, `unmute_conversation/2`, `translate_status/3`, `destroy_status/2`, `reblog/2`, `unreblog/2`, `favourite/2`, `unfavourite/2`, `favourites/2`, `statuses/3`, `home_timeline/2`, `public_timeline/2`, `hashtag_timeline/3`, `list_timeline/3` |
      | `Hunter.WebPushSubscription` | `Hunter.WebPushSubscription.create_push_subscription/3` → `Hunter.create_push_subscription/3`; also `push_subscription/1`, `update_push_subscription/2`, `delete_push_subscription/1` |

    - Typespec honesty ([#116]): entity `id` fields are now typed
      `String.t()` — what Mastodon has returned since 2.0 and what the
      structs actually held at runtime; id parameters accept
      `String.t() | non_neg_integer` as before
    - Fire-and-forget endpoints (`destroy_status`, `destroy_list`,
      `delete_media`, `clear_notification(s)`, `block_domain`,
      `unblock_domain`, `add_accounts_to_list`,
      `remove_accounts_from_list`) now return `true` as their `boolean`
      specs always promised, instead of leaking the decoded response
      body ([#116])
    - Require Elixir 1.16+ (media uploads stream the file in raw byte
      chunks, which needs the `File.stream!(path, bytes)` argument order
      introduced in 1.16); the 1.15 floor existed only for httpoison's
      transitive dependencies, which are gone
    - The HTTP stack migrated from HTTPoison/hackney to
      [Req](https://hex.pm/packages/req) ([#103]). The `:http_options`
      configuration key was replaced by `:req_options`, which takes
      [Req options](https://hexdocs.pm/req/Req.html#new/1)
    - The `Hunter.Api` behaviour and the `:hunter_api` adapter configuration
      were removed ([#103]). Entity modules now call the HTTP client
      directly; to stub Hunter in your tests, intercept requests at the HTTP
      layer with [`Req.Test`](https://hexdocs.pm/req/Req.Test.html) via
      `config :hunter, req_options: [plug: {Req.Test, MyStub}]`

  * Features
    - Notifications v2 ([#122]): `unread_count/1`, the notification
      filtering policy (`notification_policy/1`,
      `update_notification_policy/2`), notification requests
      (`notification_requests/2`, `notification_request/2`,
      `accept_notification_request/2`, `dismiss_notification_request/2`,
      bulk accept/dismiss, `notification_requests_merged?/1`) and grouped
      notifications (`grouped_notifications/2`, `notification_group/2`,
      `dismiss_notification_group/2`, `notification_group_accounts/2`,
      `grouped_unread_count/1`, new `Hunter.GroupedNotificationsResults`
      entity), all on `Hunter`
    - Web Push subscriptions ([#122]): `create_push_subscription/3`,
      `push_subscription/1`, `update_push_subscription/2` and
      `delete_push_subscription/1` on `Hunter`
    - Lists support ([#121]): `lists/1`, `list/2`, `create_list/3`,
      `update_list/3`, `destroy_list/2`, `list_accounts/3`,
      `add_accounts_to_list/3`, `remove_accounts_from_list/3`,
      `account_lists/2` and the `list_timeline/3` timeline, all exposed on
      the `Hunter` facade
    - Status parity endpoints ([#120]): `edit_status/4`, `status_history/2`,
      `status_source/2`, `bookmark/2`, `unbookmark/2`, `bookmarks/2`,
      `pin/2`, `unpin/2`, `mute_conversation/2`, `unmute_conversation/2`,
      `translate_status/3` and `statuses_by_ids/2` (all also exposed on the
      `Hunter` facade)
    - Polls ([#120]): `Hunter.poll/2` and `Hunter.vote/3`, plus
      the `poll` option on `create_status`
    - `create_status` upgrades ([#120]): `language` and `quoted_status_id`
      options; `scheduled_at` returns a `Hunter.ScheduledStatus`;
      `idempotency_key` is sent as the `Idempotency-Key` header
    - Media management ([#120]): `media_attachment/2` (processing status of
      async uploads), `update_media/3` and `delete_media/2`
    - New entities `Hunter.StatusSource` and `Hunter.StatusEdit` backing the
      status source/history endpoints ([#120])
    - Modernized the existing entity structs to the Mastodon 4.6 shapes
      ([#119]):
      - `Hunter.Status`: `text`, `edited_at`, `replies_count`, `bookmarked`,
        `pinned`, `emojis`, `poll`, `filtered`, `quote`, `quote_approval`
      - `Hunter.Account`: `uri`, `last_status_at`, `group`, `discoverable`,
        `noindex`, `suspended`, `limited`, `hide_collections`, `roles`,
        `attribution_domains`, `source`; `fields` is now a list of
        `Hunter.Field` structs
      - `Hunter.Relationship`: `showing_reblogs`, `notifying`, `languages`,
        `blocked_by`, `muting_notifications`, `muting_expires_at`,
        `requested_by`, `endorsed`, `note`
      - `Hunter.Notification`: `group_key`, `report`, `event`,
        `moderation_warning`
      - `Hunter.Tag`: `id`, `history`, `following`, `featuring`
      - `Hunter.Card`: `blurhash`, `embed_url`, `authors` (list of
        `Hunter.Card.Author`), plus `published_at`/`history` for trending
        links
      - `Hunter.Attachment`: `blurhash`, `preview_remote_url`
      - `Hunter.Emoji`: `category`
      - `Hunter.Application`: `name`, `website`, `redirect_uris`,
        `client_secret_expires_at` (the `CredentialApplication` shape)
      - `Hunter.Instance.rules` is now a list of `Hunter.Rule` structs
    - New entity structs, ready for the endpoints that return them ([#119]):
      `Hunter.Poll` (and `Hunter.Poll.Option`), `Hunter.Quote`,
      `Hunter.Filter`, `Hunter.FilterKeyword`, `Hunter.FilterStatus`,
      `Hunter.FilterResult`, `Hunter.Translation`, `Hunter.ScheduledStatus`,
      `Hunter.List`, `Hunter.Conversation`, `Hunter.Marker`,
      `Hunter.Suggestion`, `Hunter.FeaturedTag`, `Hunter.Announcement` (and
      `Hunter.Announcement.Reaction`), `Hunter.Preferences`, `Hunter.Field`,
      `Hunter.Role`, `Hunter.NotificationPolicy`,
      `Hunter.NotificationRequest`, `Hunter.NotificationGroup`,
      `Hunter.WebPushSubscription`, `Hunter.Rule`,
      `Hunter.ExtendedDescription`, `Hunter.PrivacyPolicy`,
      `Hunter.TermsOfService`, `Hunter.DomainBlock`, `Hunter.Collection`
      (and `Hunter.Collection.Item`), `Hunter.AnnualReport`

[#119]: https://github.com/milmazz/hunter/issues/119
[#120]: https://github.com/milmazz/hunter/issues/120
[#121]: https://github.com/milmazz/hunter/issues/121
[#103]: https://github.com/milmazz/hunter/issues/103
[#116]: https://github.com/milmazz/hunter/issues/116
[#122]: https://github.com/milmazz/hunter/issues/122

## v0.6.0

  * Features
    - `Hunter.log_in_oauth/3`: obtain an access token from an OAuth
      authorization code (the authorization-code grant), complementing the
      existing password-grant `Hunter.log_in/4`
    - `Hunter.Application` now records the `scopes` and `redirect_uri` the
      app was registered with (persisted by `save?: true`), so the login
      helpers can request them

  * Breaking changes
    - Require Elixir 1.15+ and Erlang/OTP 26+ (transitive dependencies of
      httpoison 3.0 no longer compile on OTP 25)
    - Major dependency upgrades: httpoison 1.x → 3.0 and poison 4.x/5.x →
      6.0; check for version conflicts with sibling dependencies in your tree
    - `Hunter.Result.hashtags` is now a list of `Hunter.Tag` structs (the
      `/api/v2/search` shape) instead of strings.
    - Removed the `follow_by_uri` function (`Hunter` and `Hunter.Account`):
      Mastodon 4.0 removed `POST /api/v1/follows`. Search for the account and
      use `Hunter.follow/2` instead.
    - Removed the `reports` listing function (`Hunter` and `Hunter.Report`):
      Mastodon removed `GET /api/v1/reports`. Filing reports via
      `Hunter.report/4` still works.
    - The `Hunter.Api` behaviour contract changed: `log_in_oauth/3` is a new
      required callback; the `follow_by_uri`/`reports`/`card_by_status`
      callbacks were removed; and `follow_request_action` now returns a
      `Hunter.Relationship` — custom API adapters need updating
    - `Hunter.Client` field `bearer_token` was renamed to `access_token` for
      consistency with other Mastodon client libraries; update
      `Hunter.Client.new(bearer_token: …)` calls to `access_token:` ([#101])
    - Removed the `card_by_status` function (`Hunter` and `Hunter.Card`):
      Mastodon 3.0 removed the endpoint. The preview card is now embedded in
      `Hunter.Status` as the `card` field ([#118])
    - `accept_follow_request/2` and `reject_follow_request/2` now call the
      documented per-id endpoints and return a `Hunter.Relationship` instead
      of a boolean; the previous implementation matched no Mastodon version
      and could only fail ([#118])
    - `Hunter.Instance` reshaped to the `GET /api/v2/instance` entity
      (`domain`, `configuration`, `contact`, …); nested objects decode as
      plain maps for now ([#118])

  * Bug fixes
    - GET/DELETE request options now travel as query-string parameters instead
      of JSON request bodies, which proxies routinely drop ([#74])
    - `Hunter.log_in/4` now requests the scopes the app was registered with;
      previously the token silently fell back to Mastodon's default `read`
      scope, making every write action fail with "This action is outside the
      authorized scopes" ([#100]). Re-run `create_app` once to refresh saved
      credentials created by older hunter versions.
    - `block_domain/2` and `unblock_domain/2` now send the `Authorization`
      header; previously they always failed with "The access token is
      invalid" ([#110])
    - `Hunter.log_in_oauth/3` now sends the `redirect_uri` in the token
      exchange; Doorkeeper rejects the authorization-code grant without it,
      so the function could never succeed. `Hunter.Application` records the
      redirect URI the app was registered with; stale saved credentials fall
      back to `urn:ietf:wg:oauth:2.0:oob` ([#112])
    - Account `emojis` now decode as `Hunter.Emoji` structs, matching the
      documented typespec; previously they were plain maps ([#107])
    - Notification dismissal uses the documented
      `POST /api/v1/notifications/:id/dismiss` path; the previous path
      matched no Mastodon version ([#118])
    - Media uploads use `POST /api/v2/media` (v1 deprecated since Mastodon
      3.1.3); large files process asynchronously and the attachment `url`
      may be `nil` until ready ([#118])

## v0.5.1

  * Bug fixes
    - Fix publishing new statuses (#14)

## v0.5.0

  * Features
    - Add Emoji entity, add new fields on Account entity
    - Fix upload_media
    - Updated some dependencies
    - Include new attributes in some Entities
    - Improve exception handling
    - Move `reblogged_by` and `favourited_by` to Account module
    - Update endpoints according to recent docs
    - Fix `create_status` and `relationships` endpoints
    - Refactor HTTP Client
    - Set default values for API base url and home
    - Fix some specs
  * Documentation
    - Hide internal details from docs
    - Fix link to server-sent events docs
    - Add more examples in the docs

## v0.4.0

  * Features
    - Update current user: `Hunter.update_credentials/2`
    - Register an application: `Hunter.create_app/5`
    - Load persisted app credentials: `Hunter.load_credentials/1`
    - Acquire access token: `Hunter.log_in/4`
    - Get who reblogged a status: `Hunter.reblogged_by/2`
    - Get who favorited a status: `Hunter.favourited_by/2`
    - Authorize follow requests: `Hunter.accept_follow_request/2`
    - Reject follow requests: `Hunter.reject_follow_request/2`
  * Documentation
    - Add more examples in the README
    - How to contribute guide
    - Code of conduct

## v0.3.0

  * Features:
    - Search for accounts or content
    - Get an account's relationship
    - Fetch user's blocks
    - Fetch a list of follow requests
    - Fetch user's mutes
    - Get instance information
    - Fetch user's notifications
    - Getting a single notification
    - Clear notifications
    - Fetch user's reports
    - Report a user
    - Get status context
    - Get a card associated with a status

## v0.2.0

  * Features:
    - Add media files
    - Fetching user's favorites
    - (un)favoriting a status

 ## v0.1.0

  * Initial release

[#74]: https://github.com/milmazz/hunter/issues/74
[#100]: https://github.com/milmazz/hunter/issues/100
[#101]: https://github.com/milmazz/hunter/issues/101
[#110]: https://github.com/milmazz/hunter/issues/110
[#112]: https://github.com/milmazz/hunter/issues/112
[#107]: https://github.com/milmazz/hunter/issues/107
[#118]: https://github.com/milmazz/hunter/issues/118
