Last active
June 7, 2020 07:00
-
-
Save amonks/9916961cc8ec9ba12013b9d4315cfe85 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Add something here to handle a new type of plex event. | |
const event_handlers = { | |
"media.resume": silent, | |
"media.pause": silent, | |
"media.scrobble": silent, | |
"media.play": verb_handler("started playing"), | |
"media.stop": silent, | |
}; | |
// silent handles a plex event by posting no message to rocket chat. | |
function silent(event) { | |
return undefined; | |
} | |
// handle_media_event makes a robot messsage describing a plex event. It uses a | |
// cache to avoid sending the same message about the same user twice in a row. | |
function handle_media_event(verbed, event) { | |
const username = user_name(event); | |
const message = make_robot_message( | |
`${username} ${verbed} ${media_name(event)}` | |
); | |
const messageString = JSON.stringify(message); | |
const mostRecentMessageStringForThisUser = | |
most_recent_messages_for_users[username]; | |
// Do not send a message if we already sent the same message for this user. | |
if (messageString === mostRecentMessageStringForThisUser) { | |
return undefined; | |
} | |
// Otherwise, set _this_ as the most recent event and send it out. | |
most_recent_messages_for_users[username] = messageString; | |
return message; | |
} | |
// We'll store in this cache a map from a user to the (stringified) most recent | |
// message we sent about that user. We'll use it to avoid sending the same | |
// message more than once in a row. | |
const most_recent_messages_for_users = {}; | |
// user_name is the name of the user that triggered the plex event. | |
function user_name(event) { | |
return event.Account.title; | |
} | |
// media_name is a nicely formatted name for the media referenced by a plex | |
// event. | |
function media_name(event) { | |
const metadata = event.Metadata; | |
const { title, grandparentTitle, parentTitle } = metadata; | |
const possibleTitles = [grandparentTitle, parentTitle, title]; | |
const titlesThatExist = possibleTitles.filter(Boolean); | |
return titlesThatExist.join(" • "); | |
} | |
// verb_handler makes an event handler for a particular verb. | |
function verb_handler(verb) { | |
return function (event) { | |
return handle_media_event(verb, event); | |
}; | |
} | |
// handle_plex_request, given a parsed request from plex, returns a message to | |
// send to the #plexhooks channel in robot, or `undefined` to send no message. | |
function handle_plex_request(plexRequest) { | |
const eventType = plexRequest.event; | |
try { | |
const handler = event_handlers[eventType]; | |
if (!handler) { | |
throw Error(`No handler defined`); | |
} | |
return handler(plexRequest); | |
} catch (e) { | |
return make_robot_message( | |
`Error handling ${codeblock(eventType)}: ${e}`, | |
plexRequest | |
); | |
} | |
} | |
// make_robot_message builds a robot message, optionally including a nice | |
// pretty code block with some metadata afterwards. | |
function make_robot_message(text, metadata) { | |
if (!metadata) { | |
return { content: { text } }; | |
} | |
return { | |
content: { | |
text: text + multiline_code_block(prettify(metadata)), | |
}, | |
}; | |
} | |
// codeblock wraps a string in backticks. | |
function codeblock(str) { | |
return "`" + str + "`"; | |
} | |
// multiline_code_block wraps a string in lines with triple-backticks. | |
function multiline_code_block(str) { | |
const codeblock = "\n```\n"; | |
return codeblock + str + codeblock; | |
} | |
// prettify turns an object into nice pretty multiline indented json. | |
function prettify(data) { | |
return JSON.stringify(data, null, 2); | |
} | |
// `data` includes many parts separated by a boundary string. One of those | |
// parts is json. We'll assume that the first part containing a "{" is the | |
// json part, and parse that. | |
function parse_json_from_first_part_of_multipart_form_data(data) { | |
const multipartBoundary = "--------------------------"; | |
const parts = data.split(multipartBoundary); | |
const jsonPart = parts.find((part) => part.includes("{")); | |
const json = jsonPart.slice( | |
jsonPart.indexOf("{"), | |
jsonPart.lastIndexOf("}") + 1 | |
); | |
return JSON.parse(json); | |
} | |
class Script { | |
process_incoming_request({ request }) { | |
try { | |
// robot.chat tries to parse json content into a property | |
// `request.content`, but it does not succeed because of plex's | |
// weird-looking requests, so we will parse it ourselves. | |
const rawContent = request.content_raw; | |
const content = parse_json_from_first_part_of_multipart_form_data( | |
rawContent | |
); | |
return handle_plex_request(content); | |
} catch (e) { | |
return make_robot_message(`Error handling plex webhook "${e}"`, request); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment