Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add administrative webhooks #18510

Merged
merged 2 commits into from
Jun 9, 2022
Merged

Add administrative webhooks #18510

merged 2 commits into from
Jun 9, 2022

Conversation

Gargron
Copy link
Member

@Gargron Gargron commented May 25, 2022

Webhooks would benefit automation through the moderation API by notifying applications about system events in real-time. They would also enable integrations with chat apps like Discord, IRC and Slack, helping moderator coordination.

Events currently supported:

  • report.created
  • account.created

The X-Hub-Signature header adopted from the WebSub spec can be optionally used to verify that the payloads are authentic.

The payload delivered to the webhook has the form of:

{
  "event": "report.created",
  "created_at": "2022-06-08T12:28:31.000Z",
  "object": {
    // The report entity would be here
  }
}

Mind that created_at at the top-level refers to the event itself, and not the object.


Screenshot 2022-06-08 at 00-38-37 New webhook - Mastodon (Dev)
Screenshot 2022-06-08 at 00-38-51 Webhooks - Mastodon (Dev)
Screenshot 2022-06-08 at 00-39-05 Webhooks - Mastodon (Dev)


This project was funded through the NGI0 Discovery Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 825322.

@Gargron Gargron added moderation Administration and moderation tooling NLnet Funded by NLnet, see https://nlnet.nl/project/Mastodon/ labels May 25, 2022
@Gargron Gargron force-pushed the feature-admin-webhooks branch 8 times, most recently from 10d190e to 2ffb811 Compare June 7, 2022 21:56
@Gargron Gargron marked this pull request as ready for review June 7, 2022 21:58
Copy link
Contributor

@ClearlyClaire ClearlyClaire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me overall, though I made some minor inline comments.

The list of implemented events is pretty limited, but there is no issue with that being extended in subsequent PRs.

Comment on lines +9 to +11
def call(*)
raise NotImplementedError
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just code quality, as I see it, defining the role of BaseService more clearly...

sidekiq_options queue: 'push', retry: 16, dead: false

def perform(webhook_id, body)
@webhook = Webhook.find(webhook_id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use find_by or catch ActiveRecord::RecordNotFound?

@ashleyhull-versent
Copy link
Contributor

ashleyhull-versent commented Jun 9, 2022

Brainstormed additional events:

  • report.resolved
  • report.sensitive
  • report.deleted
  • account.warn
  • account.freeze
  • account.forcesensitive
  • account.limit
  • account.suspend
  • account.memoriam
  • instance.silenced
  • instance.suspend
  • announcements.enabled

Other points of interest

  • sidekiq.queue.latency (over X value)

@Gargron Gargron merged commit a2871cd into main Jun 9, 2022
@Gargron Gargron deleted the feature-admin-webhooks branch June 9, 2022 20:01
kadoshita pushed a commit to kadoshita/mastodon that referenced this pull request Nov 19, 2022
* Add administrative webhooks

* Fix error when webhook is deleted before delivery worker runs
@wioxjk
Copy link

wioxjk commented Nov 20, 2022

I tried to get it to work with the Webhook-functionality provided by t2boit.io - in order to get notifications into a matrix-room.
However, it did not work. The suspicion is that this webhook is not "slack compatible" - is that right?

@Saiv46
Copy link
Contributor

Saiv46 commented Jan 4, 2023

If you wanted to make webhooks and stumbled upon this PR, since this is undocumented:

Webhooks are HTTP URLs for sending data of selected events, such as user registration, so it can be processed by other service.

When triggered by some event, instance will make HTTP POST request to configured URL with body with application/json encoding.

{
  "event": "account.created",
  "created_at": "2023-01-04T17:37:53.374Z",
  "object": // Admin::Account
}

JSON body includes event name (event), event creation time (created_at) and object that is related to event (in case of account.created - an instance of Admin::Account).

Recieved response body will be ignored and only status code used:

  • 2XX-3XX - Request successful.
  • 401, 408, 429 - Request will be retried (max 16).
  • 4XX-5XX - Request failed, no further attempts.

To be able to validate request payloads, X-Hub-Signature header can be used, which will be in a form of method=signature where method is one of the recognized algorithm names (SHA1, SHA256, SHA384, SHA512) and signature is the hexadecimal representation of the signature. Signature is computed using request payload as data and signing secret as key.

ClearlyClaire pushed a commit to ClearlyClaire/mastodon that referenced this pull request Jul 6, 2023
* Add administrative webhooks

* Fix error when webhook is deleted before delivery worker runs
@kevquirk
Copy link

Is there a facility to reference objects like report number from a webhook?

@carrotcypher
Copy link

carrotcypher commented Oct 25, 2023

This lacking documentation was an expected head scratcher for a moment. For the benefit of anyone else coming to this page and scratching their heads, here's how we go it working for Mastodon reports > Mattermost mod channel notifications for us at Fosstodon.org.

1) Create the new webhook endpoint on Mattermost

image

2) Create the new webhook event on Mastodon

image

For our needs, we only wanted it triggered for the report.created event.

3) Change the "Payload Template" of the Mastodon webhook event to:

{ "text": "Here is the text to send, and you can reference values with {{object.value}} depending on its location in the JSON tree (see below)" }

While you can refer to the Reports API JSON schema for some of the individual variables you are looking for (https://docs.joinmastodon.org/methods/reports/), we set up a listener to view the raw data ourselves and here is the (heavily redacted / fluffed) data received:

{
   "event":"report.created",
   "created_at":"2023-10-26T13:34:00.351Z",
   "object":{
      "id":"8437",
      "action_taken":false,
      "action_taken_at":null,
      "category":"violation",
      "comment":"",
      "forwarded":true,
      "created_at":"2023-10-26T13:34:00.348Z",
      "updated_at":"2023-10-26T13:34:00.348Z",
      "account":{
         "id":"123456789",
         "username":"bobisaburger",
         "domain":null,
         "created_at":"2023-07-13T04:39:22.493Z",
         "email":"bobisaburger@emailservice.com",
         "ip":"12.34.56.78",
         "confirmed":true,
         "suspended":false,
         "silenced":false,
         "sensitized":false,
         "disabled":false,
         "approved":true,
         "locale":"en",
         "invite_request":"I would love to be a member of your instance!",
         "ips":[
            {
               "ip":"12.34.56.78",
               "used_at":"2023-07-13T04:45:31.835Z"
            },
            {
               "ip":"98.76.54.32",
               "used_at":"2023-07-13T04:39:22.722Z"
            }
         ],
         "account":{
            "id":"123456789",
            "username":"bobisaburger",
            "acct":"bobisaburger",
            "display_name":"bobisaburger",
            "locked":false,
            "bot":false,
            "discoverable":null,
            "group":false,
            "created_at":"2023-07-13T00:00:00.000Z",
            "note":"",
            "url":"https://mastodonwebsite/@bobisaburger",
            "uri":"https://mastodonwebsite/users/bobisaburger",
            "avatar":"https://locationofavatar.com/image.jpg",
            "avatar_static":"https://locationofavatar.com/image.jpg",
            "header":"locationofheader.com/image.jpg",
            "header_static":"locationofheader.com/image.jpg",
            "followers_count":100,
            "following_count":200,
            "statuses_count":9,
            "last_status_at":"2023-08-05",
            "noindex":true,
            "emojis":[
               
            ],
            "roles":[
               
            ],
            "fields":[
               
            ]
         },
         "role":{
            "id":"-99",
            "name":"",
            "permissions":"65536",
            "color":"",
            "highlighted":false
         }
      },
      "target_account":{
         "id":"123454321",
         "username":"cheeseperson",
         "domain":"someothermastodonsite.com",
         "created_at":"2023-08-20T00:00:00.000Z",
         "email":null,
         "ip":null,
         "confirmed":null,
         "suspended":false,
         "silenced":false,
         "sensitized":false,
         "disabled":null,
         "approved":null,
         "locale":null,
         "invite_request":null,
         "ips":null,
         "account":{
            "id":"123454321",
            "username":"cheeseperson",
            "acct":"cheeseperson@someothermastodonsite.com",
            "display_name":"cheeseperson",
            "locked":false,
            "bot":false,
            "discoverable":false,
            "group":false,
            "created_at":"2023-08-20T00:00:00.000Z",
            "note":"",
            "url":"https://someothermastodonsite.com/@cheeseperson",
            "uri":"https://someothermastodonsite.com/users/cheeseperson",
            "avatar":"https://someothermastadonsite.com/avatars/original/missing.png",
            "avatar_static":"https://someothermastadonsite.com/avatars/original/missing.png",
            "header":"locationofheader.com/image.jpg",
            "header_static":"locationofheader.com/image.jpg",
            "followers_count":2,
            "following_count":2,
            "statuses_count":95,
            "last_status_at":"2023-10-26",
            "emojis":[
               
            ],
            "fields":[
               
            ]
         },
         "role":null
      },
      "assigned_account":null,
      "action_taken_by_account":null,
      "statuses":[
         {
            "id":"12345678987654321",
            "created_at":"2023-10-26T11:29:13.000Z",
            "in_reply_to_id":"1918282746465",
            "in_reply_to_account_id":"101010101010",
            "sensitive":false,
            "spoiler_text":"",
            "visibility":"public",
            "language":"de",
            "uri":"https://someothermastodonsite.com/users/cheeseperson/statuses/111301083360371621",
            "url":"https://someothermastodonsite.com/@cheeseperson/111301083360371621",
            "replies_count":0,
            "reblogs_count":0,
            "favourites_count":0,
            "edited_at":"2023-10-26T11:30:31.000Z",
            "content":"<p>Here is some content</p>",
            "reblog":null,
            "account":{
               "id":"123454321",
               "username":"cheeseperson",
               "acct":"cheeseperson@someothermastodonsite.com",
               "display_name":"cheeseperson",
               "locked":false,
               "bot":false,
               "discoverable":false,
               "group":false,
               "created_at":"2023-08-20T00:00:00.000Z",
               "note":"",
               "url":"https://someothermastodonsite.com/@cheeseperson",
               "uri":"https://someothermastodonsite.com/users/cheeseperson",
               "avatar":"https://someothermastadonsite.com/avatars/original/missing.png",
               "avatar_static":"https://someothermastadonsite.com/avatars/original/missing.png",
               "header":"locationofheader.com/image.jpg",
               "header_static":"locationofheader.com/image.jpg",
               "followers_count":2,
               "following_count":2,
               "statuses_count":95,
               "last_status_at":"2023-10-26",
               "emojis":[
                  
               ],
               "fields":[
                  
               ]
            },
            "media_attachments":[
               
            ],
            "mentions":[
               {
                  "id":"101010101010",
                  "username":"thirdperson",
                  "url":"https://thirdpersonsinstance.com/@thirdperson",
                  "acct":"thirdperson@emailwebsite.com"
               }
            ],
            "tags":[
               
            ],
            "emojis":[
               
            ],
            "card":null,
            "poll":null
         }
      ],
      "rules":[
         {
            "id":"2",
            "text":"Don't be a meanie!"
         }
      ]
   }
}

For our case, we wanted the following message in the Mattermost channel on each new report:

@channel report 10415 was just filed for the user BobIsACat

To achieve that, we use the fields object.id and object.target_account.account.url as follows:

{ "text": "@channel report [{{object.id}}](https://ourmastadoninstance.com/reports/{{object.id}}) was just filed for the user [{{object.target_account.account.url}}]({{object.target_account.account.url}})" }

Hope this was helpful for someone to get started with at least!

@carrotcypher
Copy link

carrotcypher commented Oct 26, 2023

@Gargron is there also an event for when a mod leaves a note on the report? Perhaps report.updated or report.note?

Alternatively, are there plans for any incoming webhooks so that moderation could be done from a third party site/service (for example, in our case we would want to add a moderator note to the report from inside a Mattermost channel)?

@ClearlyClaire
Copy link
Contributor

report.updated is triggered when the report itself is modified, but not when moderation notes are left on it. There is currently no webhook event for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
moderation Administration and moderation tooling NLnet Funded by NLnet, see https://nlnet.nl/project/Mastodon/
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants