From 87907b065e91422861165a305e9a7d677e36ed85 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Thu, 9 Jan 2020 23:24:07 -0600 Subject: [PATCH] MVP for szurubooru plugin --- bot/module/abstract.js | 1 + bot/module/szurubooru/client.js | 129 +++++++++++++++++++++++++++++++- bot/module/szurubooru/index.js | 17 ++++- 3 files changed, 144 insertions(+), 3 deletions(-) diff --git a/bot/module/abstract.js b/bot/module/abstract.js index be27358..35fc526 100644 --- a/bot/module/abstract.js +++ b/bot/module/abstract.js @@ -245,6 +245,7 @@ function init(mod, globalConfig) { mod.addRecognizedCommand(command, commandMethodName); }) logger.debug("Bound command methods for %s as %s", mod.name, mod.getRecognizedCommands()); + mod.postInit(); } exports.AbstractModule = AbstractModule diff --git a/bot/module/szurubooru/client.js b/bot/module/szurubooru/client.js index 7e07f74..5cb4ab3 100644 --- a/bot/module/szurubooru/client.js +++ b/bot/module/szurubooru/client.js @@ -1,3 +1,130 @@ /** * Szurubooru api client - */ \ No newline at end of file + */ + +let axios = require('axios'); +let { logger } = require('../../logging'); + +function getRandomInt(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive +} + +class SzurubooruClient { + + _randomSearchQueryValues = "type:image,animated,video sort:random"; + _randomSearchUrlTemplate = "%s/posts/?offset=%s&limit=%d&query=%s"; + + constructor(url, username, token) { + this.url = url; + this.baseUrl = url.replace('/api', ''); + this.username = username; + this.token = token; + this.updateRandomScope(); + } + + getRandomSearchUrl(offset, limit, tags = []) { + var searchQuery = this._randomSearchQueryValues; + if (tags !== null && tags.length > 0) { + searchQuery = this._randomSearchQueryValues + tags.join(" "); + } + return this.url + '/posts/?offset=' + offset + '&limit=' + limit + '&query=' + searchQuery; + } + + /** + * Determine a sane range of offset values + * + * @param {*} limit The content limit of a single page + * @param {*} total The number of objects to search through + */ + getValidOffsetRange(limit, total) { + let offsetOverflow = total % limit; + var offsetCeiling = (Math.round(total / limit)); + if (offsetOverflow === 0) { + offsetCeiling -= 1; + } + return [0, offsetCeiling]; + } + + + /** + * Determine a random offset based on the potential random search size + * + * Because Szurubooru doesn't randomize each sort:random search request, we must + * manually calculate a random offset for each request to simulate randomness in the + * posts returned. + * + * @param {*} self + * @param {*} imageCount + */ + getRandomSearchParameters(imageCount = null) { + let resultLimit = 10; + var offsetRange = [0, 1]; + if (imageCount === null) { + offsetRange = this.getValidOffsetRange(resultLimit, this.randomImageCount); + } else { + offsetRange = this.getValidOffsetRange(resultLimit, imageCount); + } + return [resultLimit, getRandomInt(offsetRange[0], offsetRange[1])]; + } + + + async updateRandomScope() { + let client = this; + let target = this.getRandomSearchUrl(0, 1); + await this.makeGetRequest(target).then((result) => { + client.randomImageCount = result.total; + }); + + } + + makeGetRequest(url) { + logger.debug("Making get request to %s", url); + return axios.get(url, { + headers: { + "Authorization": "Token " + Buffer.from(this.username + ':' + this.token).toString('base64'), + "Accept": "application/json" + } + }).then((response) => { + logger.debug("Recieved: %o", response.data); + return response.data; + }, (err) => { + logger.error("Unexpected client error: %o", err); + return null; + }); + return self._handle_response("GET", url, None, response) + } + + /** + * Return a random post matching the optionally provided tags. + * + * @param {*} self + * @param {*} tags + */ + getRandomPost(tags = []) { + let client = this; + var resultLimit = 0; + var randomSearchOffset = 0; + if (tags.length === 0) { + let randomSearchParameters = this.getRandomSearchParameters(); + resultLimit = randomSearchParameters[0]; + randomSearchOffset = randomSearchParameters[1]; + } + let searchUrl = this.getRandomSearchUrl(randomSearchOffset, resultLimit, tags); + return this.makeGetRequest(searchUrl) + .then((data) => { + if (data !== null) { + let limitSpace = Math.min(resultLimit, data.results.length - 1); + let randomPost = data.results[getRandomInt(0, limitSpace)]; + return client.baseUrl + '/' + randomPost.contentUrl; + } else { + return null + } + }, (err) => { + + }) + } +} + +exports.Client = SzurubooruClient; \ No newline at end of file diff --git a/bot/module/szurubooru/index.js b/bot/module/szurubooru/index.js index f11c067..3ac6cda 100644 --- a/bot/module/szurubooru/index.js +++ b/bot/module/szurubooru/index.js @@ -4,6 +4,7 @@ let { AbstractModule } = require('../abstract'); let { logger } = require('../../logging'); +let szurubooruClient = require('./client'); class SzurubooruModule extends AbstractModule { constructor() { @@ -30,7 +31,16 @@ class SzurubooruModule extends AbstractModule { '!szurubooru tags [...]' list tags similar to each '!szurubooru upload ' upload a remote file to szurubooru`; this.needConfig = true; - this.defaultCommand = 'help'; + this.defaultCommand = 'random'; + } + + postInit() { + super.postInit(); + this.client = new szurubooruClient.Client( + this.get("url"), + this.get("username"), + this.get("token") + ) } getConfigSensitiveFields() { @@ -42,7 +52,10 @@ class SzurubooruModule extends AbstractModule { */ cmd_random(event, ...args) { - + return this.client.getRandomPost().then((data) => { + logger.info("Random post: %o", data); + return data; + }); } }