Elder Scrolls
Elder Scrolls
mNo edit summary
mNo edit summary
 
Line 1: Line 1:
 
----
 
----
''This blog contains information about the Fandom Discussions API. I update it periodically from a transcluded document in my userspace called [[User:Atvelonis/Bot/Discussions API]], where I keep my bot files. If you have questions or clarifications about this documentation, please leave me a message on my [[User talk:Atvelonis|talk page]], reach out to me on Discord (Atvelonis#9495).''
+
''This blog contains information about the Fandom Discussions API. I update it periodically from a transcluded document in my userspace called [[User:Atvelonis/Bot/Discussions API]], where I keep my bot files. If you have questions or clarifications about this documentation, please leave me a message on my [[User talk:Atvelonis|talk page]] or reach out to me on [https://discord.gg/fandom Discord] (Atvelonis#9495).''
 
----
 
----
   

Latest revision as of 18:00, 30 July 2021


This blog contains information about the Fandom Discussions API. I update it periodically from a transcluded document in my userspace called User:Atvelonis/Bot/Discussions API, where I keep my bot files. If you have questions or clarifications about this documentation, please leave me a message on my talk page or reach out to me on Discord (Atvelonis#9495).


DwemerSpider

"I think they may be rebuilding themselves while we're not looking."Neloth

Main article: User:Atvelonis/Bot

This page documents parts of the Discussions API, which is part of Fandom's Nirvana API. It covers the basics of how you can use HTTP requests to make API calls on the Discussions forum, and how you can use API calls in scripts to carry out actions with a bot. I've written all the code on this page in Python.

The Discussions API is built on different software than the MediaWiki API. Unlike the MediaWiki API, the Discussions API is not officially supported. This means that Fandom can change it at any time and future versions may not be backwards compatible.

This guide is meant for wiki administrators who want to operate Discussions bots that go beyond Fandom's native DiscussionsAbuseFilter, or who want to learn about the API. If you don't like writing code, I recommend you stick with the abuse filter.

Prerequisites

  1. Create a new account to use for bot tasks. This account must have the bot usergroup to differentiate it from human accounts. Give it a name that signifies it's a bot.
  2. (Optional) Give your bot all necessary user rights. If you want to carry out privileged actions, such as deleting posts, your bot must have the threadmoderator or administrator rights. See ListGroupRights.
  3. Install the latest version of Python on your computer. You can write scripts in any programming language, but Python is relatively simple.
  4. (Optional) Locate or download a task scheduler. The preinstalled Windows Task Scheduler is fine. This is only necessary if you want your bot script to run automatically in the background.

API definition

The acronym "API" stands for "Application Programming Interface," which Wikipedia defines as follows:

"An application programming interface (API) is a computing interface that defines interactions between multiple software intermediaries. It defines the kinds of calls or requests that can be made, how to make them, the data formats that should be used, the conventions to follow, etc. It can also provide extension mechanisms so that users can extend existing functionality in various ways and to varying degrees."

This definition is informative, but technical and dense. For your purposes, you can think of an API as a way for a bot to easily use a website. The elements in an API are basically a roadmap for all the ways you can use the site. Want to delete a post? No problem, just find the right API call!

Anyone can go onto the actual Discussions forum and read or moderate posts, but it can be difficult to do certain tasks manually, especially moderation. Writing a script using the Discussions API allows a bot to do much of this tedious work. I describe specific utilities for bots in the "§ API calls" section.

The other advantage of APIs is that they're just data: if you're making an API call, you don't have to worry about the site's complicated HTML, CSS, and JavaScript. APIs are much more straightforward.

Authentication

In order to operate a bot on Discussions with the API, you first need to log in, or "authenticate" your login session with the server. I recommend a modular approach: keep your login code in its own file. This way, you can just import that file rather than putting your login code into every new script you write. The Python code snippet below shows the the basic format for API authentication.

import requests

headers = {
        'Connection': 'Keep alive',
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'Atvelonis/Bot'
}

username = 'AkulakhanBot'
password = 'XYZ'

session = requests.Session()
payload = {
        'username': username,
        'password': password
}
req = session.post('https://services.fandom.com/mobile-fandom-app/fandom-auth/login', data=payload, headers=headers)

Python's requests library allows you to easily send HTTP requests in scripts. The headers variable stores information relevant to the server. Here's what it means:

  • The connection key determines the type of network connection you're making. The value 'keep-alive' is a form of HTTP persistent connection over TCP, meaning multiple HTTP requests can be sent over the same connection.
  • The content type key determines the formatting of the HTTP request. The value 'application/x-www-form-urlencoded' signifies that when the request is written as a URL, it will be formatted "in key-value tuples separated by &, with a = between the key and the value," with special characters percent encoded (read more on Wikipedia). You can see what this looks like in the § API call structure section.
  • The user agent key provides information about the client. I use the value 'Atvelonis/Bot' so that it's clear that I'm operating a bot.

You must use the regular account password for authentication on Discussions, not the value generated on Special:BotPasswords, which is to be used for MediaWiki bot login. Best practice would be to draw login information from a separate file, rather than hardcoding it. You can do this with Python's open() function.

The API will set a fandom_session cookie that contains the bot's authentication, which must be included in any privileged requests. The requests library can create a session which will persist the cookie across all requests made with it.

For MediaWiki login (to edit articles, not use Discussions), please see the MediaWiki API login documentation.

API call structure

An API call on Discussions may be broken down into the following segments:

  • Request type – GET requests acquire information from the server; POST and PUT requests send information to the server.
  • Wiki domain – The URL of the wiki the request applies to, including the parent domain.
  • API path – Pointing the URL toward the API (/wikia.php) rather than user-facing content (/wiki).
  • Controller – Specifying the overarching type of request being sent.
  • Method – A functional request for the server specifying what exactly to do.
  • Parameters – Additional specifications for the request, narrowing down what is called upon.

The following is the generic API syntax to generate a list of n posts on a given wiki, where n is a positive integer value:

GET <wiki-domain> /wikia.php ?controller=DiscussionPost &method=getPosts &limit={n}

You can translate this into a usable URL format by filling in the bracketed information with the appropriate text:

https://elderscrolls.fandom.com/wikia.php?controller=DiscussionPost&method=getPosts&limit=3

If you enter this URL into your browser, you'll see the JSON (JavaScript Object Notation) output of the three most recent posts on the Discussions on The Elder Scrolls Wiki. You can put it into a JSON formatter to add whitespace and improve readabillity.

If you're using an API call in a script, you'll need to specify the request type. In this case, it's a GET request. To store the data of your request somewhere, just connect it to your login session (see the "§ Authentication" section).

session.get('https://elderscrolls.fandom.com/wikia.php?controller=DiscussionPost&method=getPosts', params={'limit': 3}, headers={'Accept': 'application/hal+json', 'User-Agent': 'Atvelonis/Bot'})

Or:

url = 'https://elderscrolls.fandom.com/wikia.php?controller=DiscussionPost&method=getPosts'
parameters = {'limit': 3}
headers = {
        'Accept': 'application/hal+json',
        'User-Agent': 'Atvelonis/Bot'
}
session.get(url, params=parameters, headers=headers)

The second formatting option just separates the parameters from the base URL, in case you have a lot of them. This can improve the readability of your API script.

API controllers

Controllers specify the overarching type of request you send. This is not a complete list of controllers in the Nirvana API, but covers everything in Discussions specifically:

You can append "Controller" to the end of each controller name if you want, such as ArticleCommentsController instead of ArticleComments. It doesn't affect the result of the API call.

API methods

API methods specify a function for the HTTP request to carry out. Below is a list of methods you may find useful. This list is not comprehensive, but covers all the methods you would realistically need to use with the API. To see a full list of methods, see the links in the § API controllers section. For parameters passed to these methods, see the § API endpoints section.

ArticleComments
Method Description
deletePost Delete a comment
editComment Edit an article comment
getArticleTitle Get the title of the article an article comment is posted on (based on stablePageId)
getCommentCount Get the number of article comments on an article
getComments Get a list of article comments
getThread Get a specific article comment thread
postNewCommentReply Post a reply to an article comment
postNewCommentThread Post an article comment thread
reportPost Report an article comment
undeletePost Undelete an article comment
DiscussionContribution
Method Description
deleteAll Delete all Discussions posts by a specific user
DiscussionForum
Method Description
createForum Create a Discussions category
deleteForum Delete a Discussions category
getForum Get a specific Discussions category
getForums Get a list of Discussions categories
moveThreadsIntoForum Move a set of threads into a Discussions category
updateForum Edit a Discussions category
updateForumDisplayOrder Edit the Discussions category ordering
DiscussionImages
Method Description
uploadImage Upload an image in a Discussions post
DiscussionLeaderboard
Method Description
getModeratorActions Get a list of Discussions moderators by moderation actions
getPosts Get a list of Discussions users by post count
getReports Get a list of Discussions users by report count
DiscussionModeration
Method Description
getPostListReports Get a list of reports for a specific Discussions post
getReportedPosts Get a list of all currently reported Discussions posts
reportPost Report a Discussions post
validatePostReport Approve a Discussions report
DiscussionPermalink
Method Description
getThreadByPostId Get a specific Discussions thread via its post ID
DiscussionPoll
Method Description
castVote Vote in a Discussions poll
getVoters Get a list of voters in a Discussions poll
DiscussionPost
Method Description
create Create a Discussions reply
delete Delete a Discussions reply
getPost Get a specific Discussions reply
getPosts Get a list of Discussions threads and replies (max: 100)
undelete Undelete a Discussions reply
update Edit a Discussions reply
DiscussionThread
Method Description
create Create a Discussions thread
delete Delete a Discussions thread
getThread Get a specific Discussions thread
getThreadForAnons A cached variation of getThread() used when the client is a logged-out user
getThreads Get a list of Discussions threads (max: 1000)
lock Lock a Discussions thread from commenting
undelete Undelete a Discussions thread
unlock Unlock a Discussions thread for commenting
update Edit a Discussions thread
DiscussionVote
Method Description
downVotePost Downvote a Discussions post (not implemented)
upVotePost Upvote a Discussions post
FeedsAndPosts
Method Description
getArticleNamesAndUsernames For article comments
getPopularTags Get a list of frequently used tags on Discussions threads
searchForTags Search for tags used on Discussions threads
MessageWall
Method Description
canAnonsPost Check if logged-out users can leave messages
createReply Reply to a message
createThread Create a message thread
deleteReply Delete a message thread reply
editPost Edit a message
getThread Get a specific message thread
getThreads Get a list of message threads
lockThread Lock a thread from messages/edits
reportPost Report a message
undeleteReply Undelete a message reply
unlockThread Unlock a thread for messages/edits

API endpoints

The following is a list of endpoints that you can use as parameters alongside methods in API calls. You can use endpoints to access specific pieces of information about things on Discussions, like who made a post or what its contents are. This list is not comprehensive, but contains most of the API endpoints you would find useful to moderate Discussions.

getPosts JSON layout

The JSON layout of a getPosts request will look something like the lists in the collapsible tables below. The JSON is actually just one big tree; I've broken it up on this page for readability. To see the original JSON, copy+paste the contents of an API call into a JSON formatter. I've provided short descriptions to some endpoints that may be useful.

getPosts contents
  • _links – The href (URL) values each display an API call relevant to the getPosts call
    • first
      • href – A call to the first page (0)
    • last
      • href – A call to the last page (the number of pages; the number of posts on the wiki divided by the value applied to limit, which is 10 by default)
    • next
      • href – A call to the next page (1, by default)
  • postCount – The number of posts on the Discussions; an integer value
  • readOnlyMode – true or false
  • _embedded – Contains embedded information about a particular post (or information about several posts)
    • See below for nested endpoints
  • contributors – The authors of the posts in the list generated (by default, shows the 10 most ancient authors)
    • count – An integer value
    • userInfo
      • id – The ID of the user (visible in the URL of a user's Discussions profile, or Special:LookupUser); an integer value
      • avatarUrl – The URL of the user's current avatar as uploaded on the site
      • name – Username
      • badgePermission – e.g. badge:staff, or the empty string
_embedded contents (nested within getPosts dictionary)
The following endpoints are nested under _embedded:
  • count
    • ARTICLE_COMMENT – The number of article comments on the specified wiki; an integer value
    • FORUM – The number of Discussions posts on the specified wiki; an integer value
    • WALL – The number of message wall posts on the specified wiki; an integer value
    • total – The total number of posts on the specified wiki; an integer value
  • doc:posts
    • _links
      • permalink
        • href – An API call accessing the specified post in the list
    • createdBy – Information about a post author
      • id – User ID
      • avatarUrl
      • name – Username
      • badgePermission
    • creationDate – Information about the post's creation date
      • nano – An integer value
      • epochSecond – The number of seconds since the beginning of time (January 1, 1970); an integer value
    • creatorId – An integer value
    • creatorIp – IP address; may be empty
    • forumId – Board/category; an integer value
    • forumName – The name of the board/category; a string
    • id – The post ID; a long integer value (the integer value generated for the ID differs depending on the age of posts; the method used to generate it has changed multiple times)
    • isDeleted – true or false
    • isEditable – true or false
    • isLocked – true or false
    • isReply – true or false
    • isReported – true or false
    • jsonModel – Contains attachment information
    • latestRevisionId – An integer value
    • modificationDate – A date, if edited; otherwise null
    • position – The chronological placement of the post in the context of the thread; an integer value, including the thread (starts at 1, not 0)
    • rawContent – The text of the post; a string
    • renderedContent
    • requesterId – The ID of the user making the API call, if logged in; an integer value
    • siteId – The ID of the wiki (see Special:Version); an integer value
    • threadCreatedBy – Thread author; a string
      • id – User ID
      • avatarUrl
      • name – Username
      • badgePermission
    • threadId – An integer value
    • title – The thread title, if not a reply; a string
    • upvoteCount – An integer value
    • _embedded
      • See below for nested endpoints
nested _embedded contents (nested within doc:posts)
Another endpoint called _embedded is nested underneath doc:posts. It mostly contains information about attachments in the form of the following sub-endpoints:
  • contentImages
  • userData
    • hasReported
    • hasUpvoted
    • permissions
  • attachments
    • atMentions
    • contentImages
    • openGraphs
    • polls
    • quizzes
  • thread
    • containerId
    • containerType – ARTICLE_COMMENT, FORUM, or WALL
    • creatorId
    • firstPost
      • id
      • renderedContent
      • jsonModel
      • createdBy
        • id – User ID
        • avatarUrl
        • name – Username
        • badgePermission
      • title
      • attachments
        • openGraphs
        • contentImages
        • polls
        • quizzes
        • atMentions
      • threadId
      • createdByIp
    • isEditable
    • isLocked
    • isReported
    • postCount
    • tags – Discussions tags associate content articles with threads
      • siteId
      • articleId
      • articleTitle
      • relativeUrl
      • image
    • title
  • latestRevision
    • creationDate
      • nano
      • epochSecond
    • creatorId
    • creatorIp
    • id
    • jsonModel
    • postId – An integer value (null by default)
    • rawContent
    • renderedContent

Because some endpoints are nested deep within this list, you may be required to use a combination of named and numerical indexing to store an endpoint as a variable in a script. The tree is composed of both dictionaries {} and arrays [], so be sure to get them correct. Use the JSON formatter above to determine the specifics. You can see this in action in the "§ Example script" section.

Other API parameters

The following are additional parameters that may be used in API calls.

  • cache
  • canViewHiddenPosts – true or false
  • canViewHiddenPostsInContainer – true or false
  • days – Number of days to get user/moderator data from
  • format – e.g. format=json
  • includeCounters – true or false
  • limit – To show a specific number of posts in a GET request; an integer value ≤ 100
  • page – The page specified for a getPosts call (or similar); an integer value
  • pivot – The ID of the post from which to offset page calculations
  • query – A search term (string) to use when searching for tags with searchForTags
  • sortDirection – ascending or descending
  • sortKey – creation_date, etc.
  • stablePageIds
  • userId – An integer value

API calls

The following is a list of API calls that may be useful, based on the above information about API call structure, controllers, methods, and endpoints. This is not a complete list: additional API calls may be constructed by combining parameters in various ways.

Call Type Generic syntax
Get a list of posts by a specific user. GET <wiki-domain>/wikia.php?controller=DiscussionContribution&method=getPosts&userId={userId}
Get a specific post (reply). GET <wiki-domain>/wikia.php?controller=DiscussionPost&method=getPost&postId={postId}
Get a list of posts. GET <wiki-domain>/wikia.php?controller=DiscussionPost&method=getPosts
Get a list of n posts. GET <wiki-domain>/wikia.php?controller=DiscussionPost&method=getPosts&limit={n}
Get a list of article comments. GET <wiki-domain>/wikia.php?controller=DiscussionPost&method=getPosts&containerType=ARTICLE_COMMENT
Get a list of Discussions posts. GET <wiki-domain>/wikia.php?controller=DiscussionPost&method=getPosts&containerType=FORUM
Get a list of message wall posts. GET <wiki-domain>/wikia.php?controller=DiscussionPost&method=getPosts&containerType=WALL
Get a specific thread. GET <wiki-domain>/wikia.php?controller=DiscussionThread&method=getThread&threadId={threadId}
Get a list of threads. GET <wiki-domain>/wikia.php?controller=DiscussionThread&method=getThreads
Get corresponding article names and post author usernames for a list of article comments. GET <wiki-domain>/wikia.php?controller=FeedsAndPosts&method=getArticleNamesAndUsernames
Delete all posts by a specific user. POST <wiki-domain>/wikia.php?controller=DiscussionContribution&method=deleteAll&userId={userId}
Delete a specific post. POST <wiki-domain>/wikia.php?controller=DiscussionPost&method=delete&postId={postId}
Create a thread in a specific board/category. POST <wiki-domain>/wikia.php?controller=DiscussionThread&method=create&forumId={forumId}

The Elder Scrolls Wiki does not use Message Walls or Article Comments, so these API calls do not generate anything.

Old syntax

The Discussions API syntax was modified slightly in November 2020. An example of this change is shown below:

GET <services-domain>/discussion/{siteId}/posts ⇒ 
GET <wiki-domain>/wikia.php?controller=DiscussionPost&method=getPosts
Old syntax: full

Example script

This is a mini version of a moderation script called discussions_delete.py that can be run on an individual wiki, which handles login in a separate file called core.py. discussions_delete.py scans all posts made in the past two minutes and deletes them if they contain any terms in a blacklist.

If you want this script to run automatically, you must create a task in a task scheduler on your machine that calls the script once every two minutes.

import core
import json
import requests
import time
import re
import pathlib
import sys

headers = {'Accept': 'application/hal+json', 'User-Agent': 'Atvelonis/Bot'}
wiki = 'akulakhanbot'
api = 'discussions_api'
session = core.login(api)

payload = {
        'limit': '25',
        'page': '0',
        'responseGroup': 'small',
        'reported': 'false',
        'viewableOnly': 'true'
}
req = session.get('https://'+wiki+'.fandom.com/wikia.php?controller=DiscussionPost&method=getPosts', params=payload, headers=headers)
ts = time.time()
update_interval = 120 # seconds

for post in reversed(req.json()['_embedded']['doc:posts']):
    if int(post['creationDate']['epochSecond']) > ts - update_interval:
        content = post['rawContent'].casefold()
        name = post['createdBy']['name'].casefold()
        thread_title = post['_embedded']['thread'][0]['title'].casefold()
        
        blacklist = ['Oglethorpe', 'Antidisestablishmentarianism']
        
        for term in blacklist:
            if re.search(term, content) or re.search(term, thread_title) or re.search(term, name):
                session.post('https://'+wiki+'.fandom.com/wikia.php?controller=DiscussionPost&method=delete&postId='+post_id, headers=headers)

This script uses two API calls:

  • A GET request to make a list of the 25 most recent posts made on the AkulakhanBot Wiki (akulakhanbot.fandom.com).
  • A POST request to delete anything disallowed. In this case, the script would delete any posts containing the terms "Oglethorpe" or "Antidisestablishmentarianism," but nothing else.

The script accesses API endpoints using a few different lines of code:

  • req.json()['_embedded']['doc:posts'] gets you a list of Discussions posts by accessing some nested API endpoints, the outermost "layer" of the posts themselves.
  • post['creationDate']['epochSecond'] finds if the timestamp of the current post in that list. The ts - update_interval logic compares this value to the frequency at which you're running the script to avoid duplicate scans.
  • post['_embedded']['thread'][0]['title'] is the way you access specific information about a post, like its title. The index path looks complicated, so I gave it a human-readable variable name, thread_title, and repeated this for the other variables.

Put this all together with a couple for loops and you have a functional bot script.

This script takes the burden off moderation teams because you can configure it to run 24/7. For wikis with a young audience, it can be used to enforce the community's rules against profanity. You can also modify the script to create a log of deleted posts, or only create a log and not delete anything automatically, and so on.

Security and misuse

As long as you have the correct API calls handy, it just takes a small amount of scripting knowledge to give you an incredible tool for your wiki. However, it also presents an opportunity for misuse if managed improperly. This can manifest in two ways:

  1. Moderation scripts can sometimes delete many more posts than intended, especially if they're equipped with complex regular expression (regex) functionality. For example, the regex .* means "any quantity of any character except for newlines." If this term were included in a blacklist, 100% of posts would be deleted. To avoid these sorts of errors, I suggest you use a thorough set of test cases on a test wiki before running the script live. You should not operate bot scripts unless you have a basic understanding of the API, as laid out in this guide, as well as the programming language your scripts are written in.
  2. Bot actions, especially those made automatically through the API, are difficult for human moderators to track. MediaWiki's user rights system prevents regular users from creating a bot, but it's still possible for someone to hack into a bot account with moderation privileges and use it for malicious purposes. Additionally, it's not unheard of for staff members to "go rogue" if they're particularly disgruntled over something. Make sure that your accounts have secure passwords (and ideally two-factor authentication), don't share account credentials, and don't give bot access to people you don't trust.

At the end of the day, moderation bots should serve the needs of your wiki's community. As long as most people on your wiki agree that a certain script should be operated, and as long as you also understand how to run it safely, you're in good shape. If you need help writing a bot script, consider asking your Wiki Representative for assistance, or send a query in the #api-and-scripting channel of the official Fandom/Gamepedia Discord server.

Sources

Public
Internal