Giter Site home page Giter Site logo

samliew / se-electionbot Goto Github PK

View Code? Open in Web Editor NEW
7.0 5.0 6.0 4.6 MB

A Stack Exchange/Stack Overflow election chatbot written in Node.js to handle FAQs in an election chat room

License: MIT License

JavaScript 94.04% HTML 4.20% Procfile 0.01% TypeScript 0.54% Shell 0.11% SCSS 1.10%
chat chatbot stackoverflow stackexchange elections bot nodejs

se-electionbot's Introduction

Stack Exchange Election Bot

Build GitHub GitHub commit activity

ElectionBot is a chatbot that answers commonly-asked questions in chat rooms about elections for sites on the Stack Exchange network.

The bot also sends a greeting with the election status after certain levels of room inactivity.

Please direct any queries & feedback to the developers in the development chatroom or create an issue on Github.

Examples of topics the bot can help with

General election help:

  • what is an election / how does it work

About candidate score:

  • how is the candidate score calculated
  • what is my candidate score (calculates if ownself is eligible for nomination)

About election badges:

  • what are the moderation badges
  • what are the participation badges
  • what are the editing badges
  • what are the required badges (SO-only)
  • which badges am I missing

Current election info:

  • election schedule
  • what is the election status
  • when is the election starting/ when is the next phase
  • when is the election ending
  • how many positions are there
  • how many users visited the election
  • where are the nomination comments
  • where is the election page
  • what is the election type

About cancellation:

  • why are elections cancelled
  • will the election be cancelled

About candidates:

  • do current moderators have to run
  • how many users are eligible to vote
  • how to nominate <myself\|someone\|others>
  • who are the <candidates\|nominees>
  • who are the <winners\|new moderators>
  • who of the moderators is running
  • why was a nomination removed

About voting:

  • can I vote in the election
  • how to decide who to vote for
  • how many <mods\|users> <voted\|participated> (mod override is currently disabled)
  • how do I vote
  • who should I vote for

Voting stats calculation

"how many users voted" command optionally accepts "to <timestamp>" postfix to limit the query.

If none is provided, it will default to the current date and time. The timestamp can be specified in several formats (Z, aka Zulu time indicator can be omitted for brevity):

Format Meaning
yyyy-MM-dd short timestamp, the time part will default to 00:00:00Z
yyyy-MM-dd HH-mm-ss full timestamp with a space in place of T as per RFC3339
yyyy-MM-ddTHH-mm-ssZ full timestamp as per ISO8601 standard

About moderators/moderating:

  • what are the responsibilities of a moderator
  • why should I be a moderator
  • why would anyone want to be a moderator
  • do moderators get paid
  • who are the <current\|former> moderators
  • who is the best moderator
  • could we just insert a diamond into our username

About the voting system:

  • what is Single Transferable Vote?
  • what is Meek STV?
  • where can the ballot file be found?

About the election questionnaire:

  • what is the N<st|nd|rd|th> question of the questionnaire?

ElectionBot info (requires mention):

  • help/info/ can you help
  • about
  • alive
  • who made me/ who are the developers
  • where is your source code

Other stuff:

  • roll a die
  • <toss\|flip> a coin

Privileged commands

Moderators and privileged users can also use these commands (requires mention) to help moderate the chat room:

Command Action
alive Requests a status report from the bot
announce <[new] nominees|winners> Makes the bot announce candidates (all or only new) or winners
commands Prints help for all bot commands
<brew|make> coffee [for <username>] Brew a random cup of coffee
<shutdown|terminate> Shuts down the bot in case of an emergency
fun <on|off> Switches fun mode on or off
get throttle Gets the current throttle value (seconds)
get time Gets the current UTC timestamp
greet Posts a greeting message from the bot
ignore <userId> Stops the bot from responding to a user
<mute|timeout|sleep> [N] Stops the bot from responding for N minutes
offtopic [messageId] Warns users (replies if messageId is provided) about being off-topic
post meta [pretty] Posts an official Meta announcement
say <message> Makes the bot echo a message
set throttle <N> Sets the current throttle value (seconds)
<unmute|clear timeout> Allows bot to respond if previously muted
voter report from <date|datetime> to <date|datetime> Gets a per-day report on user voting
whois <sitename> mods Lists current mods of a sitename

Developer-only commands

Users with access level set to AccessLevel.dev have access to a list of power user commands (requires @-mention):

Command Action Confirmation
chatroom Gets the current election chat room URL
debug <on|off> Switches debug mode on or off
feedback Posts a message about giving feedback to the bot
<get|report> confirmations Lists chat user ids and message builder names that are in the confirmation mode
get cron Gets a report on the current cron jobs
<get modes report|report modes> Gets a report on the current mode state
get rooms Lists the chat rooms the bot is currently joined to
impersonate <userId> Considers all messages to come from a user with userId (NB: might downlevel privileges)
join [roomId] room [roomId] Makes the bot join a room wiht roomId
leave [this|current] room [roomId] Makes the bot leave the current room or a room with roomId
rescrape Forces the bot to rescrape election data
reset election Resets the current election state and clears the scraping history
reset elections Resets election history for the current network site
set <access|level> <me|userId> <user|admin|dev> Sets access level of a user with userId
set config <env var name> <value> sets an environment variable for the duration of the running the bot
test cron Schedules a test cron job
<88 miles|delorean|timetravel> to <today|date|datetime> Adjusts the bot's internal clock to a given date
update election <announcements|badges|moderators> Forces an update to election data Reply with a correct update type
verbose <on|off> Switches verbose mode on or off
what is the candidate score for <userRef> Calculates candidate score of a user by userRef (see below)

Election results Meta post

The "post meta" command accepts an optional parameter "pretty" (or "prettify") that will force the bot to post a Markdown-formatted link instead. By default, the bot will post a one-boxed link to the Meta post.

Candidate score calculation

userRef in a candidate score request can be one of:

Value Meaning Example
<userId> User's site id 22656
@<userId> User's chat id @22656
https://<election site>.com/users/<userId>[/username] User's site link https://stackoverflow.com/users/22656/jon-skeet
https://chat.<chat host>.com/users/<userId>[/username] User's chat link https://chat.stackoverflow.com/users/22656/jon-skeet

Environment variables

All array-like values must be specified as a pipe-delimited list (i.e. A|B|C)

Variable Type Required? Default Description
ACCOUNT_EMAIL string yes - email of bot account
ACCOUNT_PASSWORD string yes - password of bot account
ADMIN_IDS number[] no - user network account ids to grant admin privileges (pipe-delimited) (mods and ROs are already privileged)
AUTO_LEAVE_ROOM boolean no true should the bot leave the room when the election has ended, after ELECTION_AFTERPARTY_MINS
AUTOSCALE_HEROKU boolean no true if hosted on Heroku, autoscale the bot to Basic if tracking an active election and DEBUG is false
CHAT_DOMAIN string no - default chat domain (stackexchange.com | stackoverflow.com)
CHAT_ROOM_ID number no - default chat room ID that the bot will join
CONTROL_ROOM_ID number no - flight control room for the bot to join
DEBUG boolean no false whether bot is in debug mode
DEFAULT_ELECTION_TIME string no 20:00:00 default election time (used for upcoming election announcements)
DEV_IDS number[] no - user network account ids to grant dev privileges (pipe-delimited)
ELECTION_AFTERPARTY_MINS string no - interval (minutes) after an election has ended before leaving the room if AUTO_LEAVE_ROOM is set
ELECTION_CHATROOM_URL string no - URL of the election chat room
ELECTION_URL string yes - URL of election page (with ID) that the bot will scrape
FEEDBACK_FORM_URL string no - URL for users to provide feedback about the bot
FUN_MODE boolean no true enable fun random responses
IGNORED_USER_IDS number[] no - user chatIds to ignore messages from (pipe-delimited)
HEROKU_API_TOKEN string no - API token to uses if hosted on Heroku for bot config updates
HEROKU_APP_NAME string no - application name if hosted on Heroku
KEEP_ALIVE boolean no false whether bot will ping itself occasionally
LOW_ACTIVITY_CHECK_MINS number no 10 interval (minutes) before bot can check room for inactivity
LOW_ACTIVITY_COUNT_THRESHOLD number no 20 bot can classify room as inactive only after these amount of messages have been sent
MAINTAINERS JSON no {"stackoverflow.com":[]} JSON map of chat domains to lists of maintainer ids
NODE_ENV string no production whether bot is in Node debug mode
PASSWORD string no - password to access non-public routes of the bot dashboard
REPO_URL string no - URL of this git repository
SCRAPE_INTERVAL_MINS number no 2 interval (minutes) to check election page for updates
SCRIPT_HOSTNAME string no - instance identifier, hostname for dashboard, also where keep-alive will ping
SHOW_PRIMARY_COUNTDOWN_AFTER number no 8 minimum number of candidates to start showing countdown to primary if the current phase is nomination
SOURCE_VERSION string no 1.0.0 added to the User-Agent header when the bot makes HTTP requests
STACK_API_KEYS string[] no - recommended Stack Exchange API key(s) (pipe-delimited)
THROTTLE_SECS number no 1 seconds before bot can send another response
TRANSCRIPT_SIZE number no 20 number of latest messages to show in the dashboard
VERBOSE boolean no false a debug variable

Flags

The bot keeps track of its internal state via a set of boolean flags:

Flag Default Description
announcedMetaPost false official Meta post announcing winners has been posted
debug false debug mode is on (moderate logging)
fun true fun mode is on
saidElectionEndingSoon false upcoming end of election has been announced
verbose false verbose mode is on (extra logging)

se-electionbot's People

Contributors

dependabot[bot] avatar double-beep avatar github-actions[bot] avatar oaphi avatar ryanmentley avatar samliew avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

se-electionbot's Issues

Move some bot moderator/privileged commands to dev-only

With the addition of some developer-only testing commands to the privileged triggers, mods on the SE network may accidentally trigger an unwanted action, e.g.: time-travelling during an active election.

Commands that may require moving to a separate dev-only menu:

  • timetravel
  • debug
  • test cron
  • get cron
  • die (currently doesn't do any good since Heroku restarts the bot instance)

We could continue using the current env variable ADMIN_IDS for dev IDs.

After time travelling, need to reset bot flags

Note to self:

config.flags.announcedWinners = false;
config.flags.saidElectionEndingSoon = false;

On startup, bot scrapes transcript and detects that the winners have already been announced.

Then we timetravel back to the end of election and try to trigger announce winners command.

This may be setting a local copy of BotConfig, so we are unable to trigger announce winners again if the election has already ended.

Consider storing a separate list of adminIds and devIds for different chat domains

Currently envars ADMIN_IDS and DEV_IDS are for our accounts on Chat.SO.

Our ids are different on Chat.SE and Chat.Meta.SE, so it makes sense to check against a different set when the bot is in these domains.

However unlikely, we do not know if a fourth chat server may be spun up in the future, so consideration may need to be made for that as well.

Also need to consider support should the bot spawn child processes for different chat servers/rooms (#61)

E.g.: my Chat.SO id is 584192, while on Chat.SE is 86504

I've made you an RO of the network test room: https://chat.stackexchange.com/rooms/info/92073/?tab=access

Add an environment variable to not autoscale to Hobby

For some elections, autoscaling to Hobby dynos simply wastes resources due to low or non-existent activity in the election chat room.

Currently, the only way to prevent autoscaling is to launch the bot in debug mode, but it has a disadvantage of posting a debug message every time the bot restarts. It would not be an issue if Heroku wouldn't have dyno cycling that happens at least once 24 hours (expected behavior), causing the bot to continuously post the debug message.

Drop Babel

Babel requires significant amount of setup for little benefit nowadays, especially given that we already harness a lot of TypeScript's features either way. I propose considering dropping Babel in favor of allowJS set to true (no extra setup, it works like this already now) and using what TS emits directly.

This discussion is inspired by an upcoming PR I'll link soon ๐Ÿ˜‡

Bot should rescrape election page for winners during election end

Right now, the bot only checks elections for changes every scrapeIntervalMins minutes (default: 5).

This means when an election ends, the bot could take an additional 5 minutes to announce the winners.

This should be automated to rescrape 30 seconds (or a period allowing for tallying of votes) following the end of the election since SO has automated the elections.

This may complicate #60

Badge IDs on each site on the network are different breaking candidate score calculation

E.g.: IDs of badges with the same name are different between the sites.

Stack Overflow named badges
https://api.stackexchange.com/docs/badges-by-name#pagesize=100&order=desc&sort=rank&filter=default&site=stackoverflow&run=true

Academia named badges
https://api.stackexchange.com/docs/badges-by-name#pagesize=100&order=desc&sort=rank&filter=default&site=academia&run=true

On bot start, we should call the API once to fetch named badges and update IDs of badges in electionBadges.

Bot instance should only listen to events for the room it is connected to

Currently, when bot is in multiple rooms, a triggering message event in any of the rooms will cause the bot to respond in the room that it's listening to.

We must be careful about this because the bot can't leave rooms by itself, so if we are testing in another room using a local instance, the main instance will still get those events and causing it to also respond in the election chat room (or all instances).

We need to get the event's room Id, and ignore it if it's not from the same room that it's listening/connected to.

A temporary fix would be to kick bot from all except one room. However this will raise automatic flags for privileged users (mods) in other rooms. Another option would be to login as that account and then leave the rooms.

Create a separate program/process to handle bot instances for each election on the network

Following an implementation of #60 (Create a separate program/process to scrape election status on all network sites),

We could have another "main" process that spins up more instances/processes for each election when an election is detected, and terminates them automatically N days after it ends or is cancelled.

This main process then could also "own" the development chatroom test instance if started by a dev.

We then may need to create a dev-only UI/API to manually start/stop instances, as well as override variables for each election instance.

Create a separate program/process to scrape election status on all network sites

Currently, the setting up of this bot is done manually by changing environment vars in Heroku:

  • Set CHAT_DOMAIN, if election is on another site on the Stack Exchange network
  • Find election chat room, or default chat room for that site, set CHAT_ROOM_ID to that
  • Set ELECTION_PAGE_URL to the active election page
  • Set ELECTION_QA_URL, to override if the bot couldn't detect the correct site meta question link

If we can create a program to scrape all network sites' election pages, and store the status in a persistent database, we could potentially use this to automatically spin up a new instance for that site if a scheduled election is detected (usually the announcement on per-site meta comes later, although they are automating the election process - so look out for changes).

Maybe we don't need to find/scrape default chatroom urls for each site, since that is unlikely to change. The list can be fetched from https://api.stackexchange.com/docs/sites#filter=default&run=true, and the room with the most users & messages should be the default (e.g.: https://chat.stackexchange.com/?tab=site&sort=people&host=serverfault.com).

But it would be nice for the bot to detect the election chat room if there is one, using the following methods (in order):

Automatically detect election permalink/election number

Currently the election permalink/number is manually found by trial and error, ever since the past election history page is no longer accessible during an active election.

i.e. https://stackoverflow.com/election/12

Currently, this is then inserted into the bot's environment variable to direct the bot to scrape the right page.

We need a method to automatically find the current active election's permalink/number if the bot scrapes /election for each site and finds an active election, for automation of the bot #60 and #61.

Undefined behaviour in some cases when calculating candidate score

There are some cases that the candidate score does not handle properly right now that we need to address:

  • There was an error 503 response from the API (it actually seems like with the upgraded network id getter the risk of hitting API throttle got higher) which caused the bot to respond with this (lack of whitespace is on me, though :( - fixed by cf55705):

RESPONSE Wow! You have a maximum candidate score of 40!Alas, the nomination period is over Hope to see your candidature next election!

The response has been building up correctly - the real score should've been 34/40 with 6 badges missing and rep maxed out:

Missing Badges: Electorate,Marshal,Reviewer,Steward,Investor,Copy Editor

Not sure of the root cause yet, will investigate

  • Lacking an account on the site causes the bot to respond with a calculation error. That's kind of expected, but we could look into returning a user-friendly message in this case (tested on me by accident - forgot that I do not have an account on Academia). Not sure if it is easy to distinguish between an API failure and a missing account, though.

  • We should also guard against null from the utility fetching network accounts ( 312c1ff fixes this) - it does not help us much if an error happens at this point, but it reduces log cluttering and allows the utility to gracefully exit

Test: Defaulting to Chat.SE for network sites with non-SE domains - failing when SO election

A test is failing on a certain condition:

  1) Election
       getters
         chatDomain
           should default to stackexchange.com for network sites with non-SE domains:

      AssertionError: expected 'stackoverflow.com' to equal 'stackexchange.com'
      + expected - actual

      -stackoverflow.com
      +stackexchange.com

Config variables when test fails:

CHAT_DOMAIN=stackoverflow.com
CHAT_ROOM_ID=190503
DEBUG=true
ELECTION_URL=https://stackoverflow.com/election/13

Removing the above values for CHAT_DOMAIN and CHAT_ROOM_ID passes the test.

Return a dedicated error page on invalid election data

Currently, if the election data ends up invalid, depending on the occurrence of the check, the server is either not started up at all (if the failure happens early or crashes with a 500 on subsequent connects if the rescraper lead to an invalid election state. Latest to date downtime of the bot resulted from Stack Exchange breaking access to nomination pages - we want to avoid crashing if that happens as we can still serve some useful info in the meantime and periodically rescrape to ensure we are up and running as soon as the issue is fixed.

Already working on, documenting to keep track of the updates.

There are two sub-issues that need to be solved as part of this issue:

  1. The server is started way after Election#validate method is called, and the latter exits early
  2. The ScheduledAnnouncement#rescrape method ends up breaking the bot if the election state is invalid

Message guards should cross-reference all other guard matches

Currently, guard unit tests are added and cross-referenced manually. We should auto-generate cross-referencing tests instead.

Solving this will also allow us to move forward with removing the rest of the if...else statements in favor of a reducer.

Weird cancellation text in HTML

Something weird happened 2 days after Tor has been cancelled - both dev and prod instances finally announced cancellations (which is strange by itself), but not only that, the text contained HTML for some reason: https://chat.stackexchange.com/transcript/message/61312901#61312901

Apparently, this is because SE uses a different notice template when the election is cancelled in the nomination phase and is not a pro tempore election. Compare the notice to Joomla's election, which is scraped correctly:

ice_screenshot_20220608-231141

And this is what we got on French, for example:

ice_screenshot_20220608-231313

Add a response for BLT file format questions

When the election ends, a ballot file becomes available - we should add responses to what it is and how to find it (even if in a form of an annotated link to OpaVote's website). Mostly a note to not forget to implement it until the end of the election phase (to have some time in advance - till October 22nd).

Ensure PRs from outside contributors do not fail the build

As per what transpired during the approval of the PR #147 , we need to tweak the GitHub workflow action to avoid failing the build with no options. The failure is related to secrets not being passed to normal pull_request workflows as per the docs:

With the exception of GITHUB_TOKEN, secrets are not passed to the runner when a workflow is triggered from a forked repository. The permissions for the GITHUB_TOKEN in forked repositories is read-only.

The easiest way to solve this is to switch to pull_request_target but comes with its own set of issues, see
Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests.

The most promising workflow is to use types: [labeled] that runs upon changing labels on the PR
and create a "safe" (or similar) label as external PRs have to be closely reviewed either way.

Bot needs to either change rooms (leave old), or restart process if after rescraping an election chat room is found

(Non-debug/Production mode only)

As election chat rooms are manually created by a CM and then linked on the election page, the bot may have been in a default room and needs to switch to the official election chat room.

To do this we have these options:

  • Leave the current room, and join a different room
  • Restart the Heroku dyno (or exit the process, or throw a fatal error)
  • Change an environment variable via the Heroku API

getSiteUserIdFromChatStackExchangeId network user id lookup is broken

getSiteUserIdFromChatStackExchangeId function relies on scraping the profile page of the user to get the network user id in case it did not get one from the "linked site". Unfortunately, it is now broken (as indicated by the failing CI) - I highly suspect this is due to the recent change to profile pages that SE made (see MSE post).

I already know how to fix it and will make a PR a bit later, but opening the issue for documentation purposes ๐Ÿ˜‡

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.