mami/src/mami.js/flashii.js
2025-05-19 00:16:41 +00:00

270 lines
9.2 KiB
JavaScript

#include xhr.js
const Flashii = function(baseUrl) {
if(typeof baseUrl !== 'string')
throw new Error('baseUrl must be a string');
if(baseUrl.startsWith('//'))
baseUrl = window.location.protocol + baseUrl;
const sortArray = typeof Array.prototype.toSorted === 'function'
? array => array.toSorted()
: array => {
array = array.slice();
array.sort();
return array;
};
const convertFields = fields => {
if(fields === true)
return '*';
if(Array.isArray(fields))
return sortArray(fields).join(',');
if(typeof fields === 'string')
return fields;
return null;
};
const createUrl = (path, fields=null) => {
const url = new URL(baseUrl + path);
if(fields)
url.searchParams.set('fields', convertFields(fields));
return url;
};
const send = async ({
method,
path,
fresh=false,
params=null,
fields=null,
body=null,
headers=null,
type='json',
authed=false,
}) => {
const url = createUrl(path, fields);
if(params)
for(const name in params) {
if(name === 'fields' || params[name] === null || params[name] === undefined)
continue;
url.searchParams.set(name, params[name]);
}
headers ??= {};
if(fresh) headers['Cache-Control'] = 'no-cache';
return await $xhr.send(method, url, { type, headers, authed }, body);
};
const fii = {};
fii.v1 = {};
const verifyChatServerId = id => {
if(typeof id === 'number')
id = id.toString();
if(/^([^0-9]+)$/gu.test(id))
throw new Error('id argument is not an acceptable chat server id.');
return id;
};
fii.v1.chat = {};
fii.v1.chat.login = function({ redirect=null, assign=true }={}) {
return new Promise(resolve => {
redirect ??= `${location.protocol}//${location.host}`;
if(typeof redirect !== 'string')
throw new Error('redirect must a string.');
const url = createUrl('/v1/chat/login');
url.searchParams.set('redirect', redirect);
// intentionally does not resolve
if(assign)
location.assign(url);
else
resolve(url);
});
};
fii.v1.chat.servers = async function({ proto=null, secure=null, fields=null, fresh=false }={}) {
const params = {};
if(proto !== null && typeof proto !== 'string')
throw new Error('proto must be a string or null');
params['proto'] = proto;
if(secure !== null)
params['secure'] = secure ? '1' : '0';
const { status, body } = await send({ method: 'GET', path: '/v1/chat/servers', fields, fresh, params });
if(status === 400)
throw new Error('An argument contains an unsupported value.');
if(status > 299)
throw new Error(`Failed to fetch chat server list with error code ${status}.`);
return body;
};
fii.v1.chat.servers.server = async function({ id, fields=null, fresh=false }) {
id = verifyChatServerId(id);
const { status, body } = await send({ method: 'GET', path: `/v1/chat/servers/${id}`, fields, fresh });
if(status === 400)
throw new Error('Fields argument contains unsupported value.');
if(status === 404)
throw new Error('No chat server with that id could be found.');
if(status > 299)
throw new Error(`Failed to fetch chat server with error code ${status}.`);
return body;
};
fii.v1.chat.servers.recommended = async function({ proto, secure=null, fields=null, fresh=false }) {
if(typeof proto !== 'string')
throw new Error('proto must be a string');
const params = { proto };
if(secure !== null)
params['secure'] = secure ? '1' : '0';
const { status, body } = await send({ method: 'GET', path: '/v1/chat/servers/recommended', fields, fresh, params });
if(status === 400)
throw new Error('An argument contains an unsupported value.');
if(status === 404)
throw new Error('Was unable to determine a server based on the requested parameters.');
if(status > 299)
throw new Error(`Failed to fetch recommended chat server with error code ${status}.`);
return body;
};
fii.v1.chat.token = async function() {
const { status, body } = await send({ method: 'GET', path: '/v1/chat/token', authed: true, fresh: true });
if(status === 403)
throw new Error('You must be logged in to use chat.');
if(status > 299)
throw new Error(`Failed to fetch authorization token with error code ${status}.`);
return body;
};
const verifyColourPresetName = name => {
if(/^([^A-Za-z0-9\-_]+)$/gu.test(name))
throw new Error('name argument is not an acceptable colour preset name.');
return name;
};
fii.v1.colours = {};
fii.v1.colours.presets = async function({ fields=null, fresh=false }={}) {
const { status, body } = await send({ method: 'GET', path: '/v1/colours/presets', fields, fresh });
if(status === 400)
throw new Error('Fields argument contains unsupported value.');
if(status > 299)
throw new Error(`Failed to fetch colour presets with error code ${status}.`);
return body;
};
fii.v1.colours.presets.preset = async function({ name, fields=null, fresh=false }) {
name = verifyColourPresetName(name);
const { status, body } = await send({ method: 'GET', path: `/v1/colours/presets/${name}`, fields, fresh });
if(status === 400)
throw new Error('Fields argument contains unsupported value.');
if(status === 404)
throw new Error('Requested colour preset does not exist.');
if(status > 299)
throw new Error(`Failed to fetch colour preset "${name}" with error code ${status}.`);
return body;
};
const verifyEmoticonId = id => {
if(typeof id === 'number')
id = id.toString();
if(/^([^0-9]+)$/gu.test(id))
throw new Error('id argument is not an acceptable emoticon id.');
return id;
};
fii.v1.emotes = async function({ fields=null, fresh=false }={}) {
const { status, body } = await send({ method: 'GET', path: '/v1/emotes', fields, fresh });
if(status === 400)
throw new Error('Fields argument contains unsupported value.');
if(status > 299)
throw new Error(`Failed to fetch emoticons with error code ${status}.`);
return body;
};
fii.v1.emotes.emote = async function({ id, fields=null, fresh=false }) {
id = verifyEmoticonId(id);
const { status, body } = await send({ method: 'GET', path: `/v1/emotes/${id}`, fields, fresh });
if(status === 400)
throw new Error('Fields argument contains unsupported value.');
if(status === 404)
throw new Error('Requested emoticon does not exist.');
if(status > 299)
throw new Error(`Failed to fetch emoticon "${id}" with error code ${status}.`);
return body;
};
fii.v1.emotes.emote.strings = async function({ id, fields=null, fresh=false }) {
id = verifyEmoticonId(id);
const { status, body } = await send({ method: 'GET', path: `/v1/emotes/${id}/strings`, fields, fresh });
if(status === 400)
throw new Error('Fields argument contains unsupported value.');
if(status === 404)
throw new Error('Requested emoticon does not exist.');
if(status > 299)
throw new Error(`Failed to fetch emoticon "${id}" with error code ${status}.`);
return body;
};
const verifyKaomojiId = id => {
if(typeof id === 'number')
id = id.toString();
if(/^([^0-9]+)$/gu.test(id))
throw new Error('id argument is not an acceptable kaomoji id.');
return id;
};
fii.v1.kaomoji = async function({ as=null, fields=null, fresh=false }={}) {
const { status, body } = await send({ method: 'GET', path: '/v1/kaomoji', params: { as }, fields, fresh });
if(status === 400)
throw new Error('As or fields argument contains unsupported value.');
if(status > 299)
throw new Error(`Failed to fetch kaomoji with error code ${status}.`);
return body;
};
fii.v1.kaomoji.kaomoji = async function({ id, fields=null, fresh=false }) {
id = verifyKaomojiId(id);
const { status, body } = await send({ method: 'GET', path: `/v1/kaomoji/${id}`, fields, fresh });
if(status === 400)
throw new Error('Fields argument contains unsupported value.');
if(status === 404)
throw new Error('Requested kaomoji does not exist.');
if(status > 299)
throw new Error(`Failed to fetch kaomoji "${id}" with error code ${status}.`);
return body;
};
return fii;
};