UNPKG

@cutls/megalodon

Version:

Mastodon, Pleroma, Misskey API client for node.js and browser

722 lines (721 loc) 19.3 kB
var __awaiter = (this && this.__awaiter) || ((thisArg, _arguments, P, generator) => { function adopt(value) { return value instanceof P ? value : new P((resolve) => { resolve(value) }) } return new (P || (P = Promise))((resolve, reject) => { function fulfilled(value) { try { step(generator.next(value)) } catch (e) { reject(e) } } function rejected(value) { try { step(generator['throw'](value)) } catch (e) { reject(e) } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected) } step((generator = generator.apply(thisArg, _arguments || [])).next()) }) }) var __generator = (this && this.__generator) || ((thisArg, body) => { var _ = { label: 0, sent: () => { if (t[0] & 1) throw t[1] return t[1] }, trys: [], ops: [] }, f, y, t, g return ( (g = { next: verb(0), throw: verb(1), return: verb(2) }), typeof Symbol === 'function' && (g[Symbol.iterator] = function () { return this }), g ) function verb(n) { return (v) => step([n, v]) } function step(op) { if (f) throw new TypeError('Generator is already executing.') while ((g && ((g = 0), op[0] && (_ = 0)), _)) try { if (((f = 1), y && (t = op[0] & 2 ? y['return'] : op[0] ? y['throw'] || ((t = y['return']) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done)) return t if (((y = 0), t)) op = [op[0] & 2, t.value] switch (op[0]) { case 0: case 1: t = op break case 4: _.label++ return { value: op[1], done: false } case 5: _.label++ y = op[1] op = [0] continue case 7: op = _.ops.pop() _.trys.pop() continue default: if (!((t = _.trys), (t = t.length > 0 && t[t.length - 1])) && (op[0] === 6 || op[0] === 2)) { _ = 0 continue } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1] break } if (op[0] === 6 && _.label < t[1]) { _.label = t[1] t = op break } if (t && _.label < t[2]) { _.label = t[2] _.ops.push(op) break } if (t[2]) _.ops.pop() _.trys.pop() continue } op = body.call(thisArg, _) } catch (e) { op = [6, e] y = 0 } finally { f = t = 0 } if (op[0] & 5) throw op[1] return { value: op[0] ? op[1] : void 0, done: true } } }) var __read = (this && this.__read) || ((o, n) => { var m = typeof Symbol === 'function' && o[Symbol.iterator] if (!m) return o var i = m.call(o), r, ar = [], e try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value) } catch (error) { e = { error: error } } finally { try { if (r && !r.done && (m = i['return'])) m.call(i) } finally { if (e) throw e.error } } return ar }) var __values = (this && this.__values) || ((o) => { var s = typeof Symbol === 'function' && Symbol.iterator, m = s && o[s], i = 0 if (m) return m.call(o) if (o && typeof o.length === 'number') return { next: () => { if (o && i >= o.length) o = void 0 return { value: o && o[i++], done: !o } } } throw new TypeError(s ? 'Object is not iterable.' : 'Symbol.iterator is not defined.') }) import axios from 'axios' import dayjs from 'dayjs' import FormData from 'form-data' import { DEFAULT_UA } from '../default' import WebSocket from './web_socket' import MisskeyNotificationType from './notification' import NotificationType from '../notification' import { isBrowser } from '../default' import Autolinker from 'autolinker' export var isEmojiArr = (item) => Array.isArray(item) function autoLinker(input, host) { return Autolinker.link(input, { hashtag: 'twitter', mention: 'twitter', email: false, stripPrefix: false, replaceFn: (match) => { switch (match.type) { case 'url': return true case 'mention': return '<a href="https://'.concat(host, '/@').concat(encodeURIComponent(match.getMention()), '" target="_blank">@').concat(match.getMention(), '</a>') case 'hashtag': return '<a href="https://'.concat(host, '/tags/').concat(encodeURIComponent(match.getHashtag()), '" target="_blank">#').concat(match.getHashtag(), '</a>') } return false } }) } var MisskeyAPI ;((MisskeyAPI) => { var Converter ;((Converter) => { Converter.emoji = (e) => ({ shortcode: e.name, static_url: e.url, url: e.url, visible_in_picker: true }) Converter.emojiConverter = (e) => { if (!e) return [] if (isEmojiArr(e)) return e var emojiArr = Object.entries(e).map((_a) => { var _b = __read(_a, 2), key = _b[0], value = _b[1] return { name: key, host: null, url: value, aliases: [] } }) return emojiArr } Converter.user = (u, host) => { host = host.replace('https://', '') var acct = u.username var acctUrl = 'https://'.concat(host || u.host || 'example.com', '/@').concat(u.username) if (u.host) { acct = ''.concat(u.username, '@').concat(u.host) acctUrl = 'https://'.concat(u.host, '/@').concat(u.username) } return { id: u.id, username: u.username, acct: acct, display_name: u.name || u.username, locked: false, created_at: new Date().toISOString(), followers_count: 0, following_count: 0, statuses_count: 0, note: '', url: acctUrl, avatar: u.avatarUrl, avatar_static: u.avatarUrl, header: u.avatarUrl, header_static: u.avatarUrl, emojis: Converter.emojiConverter(u.emojis).map((e) => Converter.emoji(e)), moved: null, fields: [], bot: false, group: false, discoverable: false, noindex: false, suspended: false, limited: false } } Converter.userDetail = (u, host) => { var acct = u.username host = host.replace('https://', '') var acctUrl = 'https://'.concat(host || u.host || 'example.com', '/@').concat(u.username) if (u.host) { acct = ''.concat(u.username, '@').concat(u.host) acctUrl = 'https://'.concat(u.host, '/@').concat(u.username) } return { id: u.id, username: u.username, acct: acct, display_name: u.name || u.username, locked: !!u.isLocked, created_at: u.createdAt || new Date().toISOString(), followers_count: u.followersCount || 0, following_count: u.followingCount || 0, statuses_count: u.notesCount || 0, note: u.description || '', url: acctUrl, avatar: u.avatarUrl || 'https://http.cat/404', avatar_static: u.avatarUrl || 'https://http.cat/404', header: u.bannerUrl || u.avatarUrl || 'https://http.cat/404', header_static: u.bannerUrl || u.avatarUrl || 'https://http.cat/404', emojis: Converter.emojiConverter(u.emojis).map((e) => Converter.emoji(e)), moved: null, fields: [], bot: u.isBot || false, group: false, discoverable: false, noindex: false, suspended: false, limited: false } } Converter.visibility = (v) => { switch (v) { case 'public': return v case 'home': return 'unlisted' case 'followers': return 'private' case 'specified': return 'direct' } } Converter.encodeVisibility = (v) => { switch (v) { case 'public': return v case 'unlisted': return 'home' case 'private': return 'followers' case 'direct': return 'specified' } } Converter.fileType = (s) => { if (s === 'image/gif') { return 'gifv' } if (s.includes('image')) { return 'image' } if (s.includes('video')) { return 'video' } if (s.includes('audio')) { return 'audio' } return 'unknown' } Converter.file = (f) => ({ id: f.id, type: Converter.fileType(f.type), url: f.url, remote_url: f.url, preview_url: f.thumbnailUrl, text_url: f.url, meta: { width: f.properties.width, height: f.properties.height }, description: null, blurhash: null }) Converter.follower = (f, host) => Converter.user(f.follower, host) Converter.following = (f, host) => Converter.user(f.followee, host) Converter.relation = (r) => ({ id: r.id, following: r.isFollowing, followed_by: r.isFollowed, blocking: r.isBlocking, blocked_by: r.isBlocked, muting: r.isMuted, muting_notifications: false, requested: r.hasPendingFollowRequestFromYou, domain_blocking: false, showing_reblogs: true, endorsed: false, notifying: false, note: '' }) Converter.choice = (c) => ({ title: c.text, votes_count: c.votes }) Converter.poll = (p) => { var now = dayjs() var expire = dayjs(p.expiresAt) var count = p.choices.reduce((sum, choice) => sum + choice.votes, 0) return { id: '', expires_at: p.expiresAt, expired: now.isAfter(expire), multiple: p.multiple, votes_count: count, options: p.choices.map((c) => Converter.choice(c)), voted: p.choices.some((c) => c.isVoted) } } Converter.note = (n, host) => { host = host.replace('https://', '') var text = n.text ? n.text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/`/g, '&#x60;').replace(/\r?\n/g, '<br>') : '' var html = autoLinker(text, host) return { id: n.id, uri: n.uri ? n.uri : 'https://'.concat(host, '/notes/').concat(n.id), url: n.uri ? n.uri : 'https://'.concat(host, '/notes/').concat(n.id), account: Converter.user(n.user, host), in_reply_to_id: n.replyId, in_reply_to_account_id: null, reblog: n.renote ? Converter.note(n.renote, host) : null, content: html, plain_content: n.text ? n.text : null, created_at: n.createdAt, edited_at: null, emojis: Converter.emojiConverter(n.emojis).map((e) => Converter.emoji(e)), replies_count: n.repliesCount, reblogs_count: n.renoteCount, favourites_count: 0, reblogged: false, favourited: !!n.myReaction, muted: false, sensitive: n.files ? n.files.some((f) => f.isSensitive) : false, spoiler_text: n.cw ? n.cw : '', visibility: Converter.visibility(n.visibility), media_attachments: n.files ? n.files.map((f) => Converter.file(f)) : [], mentions: [], tags: [], card: null, poll: n.poll ? Converter.poll(n.poll) : null, application: null, language: null, pinned: null, emoji_reactions: Converter.mapReactions(host, n.reactions, n.myReaction || '', n.reactionEmojis || {}), bookmarked: false, quote: !!(n.renote && n.text), quote_status_misskey: n.renote && n.text ? Converter.note(n.renote, host) : null } } Converter.mapReactions = (host, r, myReaction, emojiData) => { if (isEmojiArr(emojiData)) { return emojiData.map((e) => ({ count: 0, me: false, name: e.name, url: e.url, static_url: e.url })) } return Object.keys(r).map((key) => { var keyObj = key.replace(/:/g, '') var shortcode = key.replace(/[:@.]/g, '') var isCustomEmoji = shortcode !== key if (myReaction && key === myReaction) { return { count: r[key], me: true, name: shortcode, url: isCustomEmoji ? emojiData[keyObj] || 'https://'.concat(host, '/emoji/').concat(shortcode, '.webp') : undefined, static_url: isCustomEmoji ? emojiData[keyObj] || 'https://'.concat(host, '/emoji/').concat(shortcode, '.webp') : undefined } } return { count: r[key], me: false, name: shortcode, url: isCustomEmoji ? emojiData[keyObj] || 'https://'.concat(host, '/emoji/').concat(shortcode, '.webp') : undefined, static_url: isCustomEmoji ? emojiData[keyObj] || 'https://'.concat(host, '/emoji/').concat(shortcode, '.webp') : undefined } }) } Converter.reactions = (r, host, emojiData) => { var e_1, _a var result = [] if (isEmojiArr(emojiData)) { return emojiData.map((e) => ({ count: 0, me: false, name: e.name, url: e.url, static_url: e.url })) } var _loop_1 = (e) => { var shortcode = e.type.replace(/[:@.]/g, '') var isCustomEmoji = shortcode !== e.type var i = result.findIndex((res) => res.name === e.type) if (i >= 0) { result[i].count++ } else { result.push({ count: 1, me: false, name: shortcode, url: isCustomEmoji ? emojiData[shortcode] || 'https://'.concat(host, '/emoji/').concat(shortcode, '.webp') : undefined, static_url: isCustomEmoji ? emojiData[shortcode] || 'https://'.concat(host, '/emoji/').concat(shortcode, '.webp') : undefined }) } } try { for (var r_1 = __values(r), r_1_1 = r_1.next(); !r_1_1.done; r_1_1 = r_1.next()) { var e = r_1_1.value _loop_1(e) } } catch (e_1_1) { e_1 = { error: e_1_1 } } finally { try { if (r_1_1 && !r_1_1.done && (_a = r_1.return)) _a.call(r_1) } finally { if (e_1) throw e_1.error } } return result } Converter.noteToConversation = (n, host) => { var accounts = [Converter.user(n.user, host)] if (n.reply) { accounts.push(Converter.user(n.reply.user, host)) } return { id: n.id, accounts: accounts, last_status: Converter.note(n, host), unread: false } } Converter.list = (l) => ({ id: l.id, title: l.name, replies_policy: null }) Converter.encodeNotificationType = (e) => { switch (e) { case NotificationType.Follow: return MisskeyNotificationType.Follow case NotificationType.Mention: return MisskeyNotificationType.Reply case NotificationType.Favourite: case NotificationType.Reaction: return MisskeyNotificationType.Reaction case NotificationType.Reblog: return MisskeyNotificationType.Renote case NotificationType.PollVote: return MisskeyNotificationType.PollVote case NotificationType.FollowRequest: return MisskeyNotificationType.ReceiveFollowRequest default: return e } } Converter.decodeNotificationType = (e) => { switch (e) { case MisskeyNotificationType.Follow: return NotificationType.Follow case MisskeyNotificationType.Mention: case MisskeyNotificationType.Reply: return NotificationType.Mention case MisskeyNotificationType.Renote: case MisskeyNotificationType.Quote: return NotificationType.Reblog case MisskeyNotificationType.Reaction: return NotificationType.Reaction case MisskeyNotificationType.PollVote: return NotificationType.PollVote case MisskeyNotificationType.ReceiveFollowRequest: return NotificationType.FollowRequest case MisskeyNotificationType.FollowRequestAccepted: return NotificationType.Follow default: return e } } var modelOfAcct = { id: '1', username: 'none', acct: 'none', display_name: 'none', locked: true, bot: true, discoverable: false, group: false, created_at: '1971-01-01T00:00:00.000Z', note: '', url: 'https://http.cat/404', avatar: 'https://http.cat/404', avatar_static: 'https://http.cat/404', header: 'https://http.cat/404', header_static: 'https://http.cat/404', followers_count: -1, following_count: 0, statuses_count: 0, noindex: true, emojis: [], fields: [], moved: null, suspended: false, limited: false } Converter.notification = (n, host) => { var notification = { id: n.id, account: n.user ? Converter.user(n.user, host) : modelOfAcct, created_at: n.createdAt, type: Converter.decodeNotificationType(n.type) } if (n.note) { notification = Object.assign(notification, { status: Converter.note(n.note, host) }) } if (n.reaction) { notification = Object.assign(notification, { emoji: n.reaction }) } return notification } Converter.stats = (s) => ({ user_count: s.usersCount, status_count: s.notesCount, domain_count: s.instances }) Converter.meta = (m, s) => { var wss = m.uri.replace(/^https:\/\//, 'wss://') return { uri: m.uri, title: m.name, description: m.description, email: m.maintainerEmail, version: m.version, thumbnail: m.bannerUrl, urls: { streaming_api: ''.concat(wss, '/streaming') }, stats: Converter.stats(s), languages: m.langs, contact_account: undefined, registrations: !m.disableRegistration, approval_required: false, configuration: { statuses: { max_characters: m.maxNoteTextLength } } } } Converter.hashtag = (h) => ({ name: h.tag, url: h.tag, history: [], following: false }) })((Converter = MisskeyAPI.Converter || (MisskeyAPI.Converter = {}))) MisskeyAPI.DEFAULT_SCOPE = [ 'read:account', 'write:account', 'read:blocks', 'write:blocks', 'read:drive', 'write:drive', 'read:favorites', 'write:favorites', 'read:following', 'write:following', 'read:mutes', 'write:mutes', 'write:notes', 'read:notifications', 'write:notifications', 'read:reactions', 'write:reactions', 'write:votes' ] var Client = (() => { function Client(baseUrl, accessToken, userAgent) { if (userAgent === void 0) { userAgent = DEFAULT_UA } this.accessToken = accessToken this.baseUrl = baseUrl this.userAgent = userAgent this.abortController = new AbortController() axios.defaults.signal = this.abortController.signal } Client.prototype.post = function (path_1) { return __awaiter(this, arguments, void 0, function (path, params, headers) { var options, bodyParams if (params === void 0) { params = {} } if (headers === void 0) { headers = {} } return __generator(this, function (_a) { options = { headers: headers, maxContentLength: Infinity, maxBodyLength: Infinity } bodyParams = params if (this.accessToken) { if (params instanceof FormData) { bodyParams.set('limit', parseInt(bodyParams.get('limit'), 10)) bodyParams.append('i', this.accessToken) } else { params.limit = parseInt(params.limit, 10) bodyParams = Object.assign(params, { i: this.accessToken }) } } return [ 2, axios.post(this.baseUrl + path, bodyParams, options).then((resp) => { var res = { data: resp.data, status: resp.status, statusText: resp.statusText, headers: resp.headers } return res }) ] }) }) } Client.prototype.cancel = function () { return this.abortController.abort() } Client.prototype.socket = function (channel, listId) { if (!this.accessToken) { throw new Error('accessToken is required') } var url = this.baseUrl.replace('https://', 'wss://') + '/streaming' var streaming = new WebSocket(url, channel, this.accessToken, listId, this.userAgent) if (!isBrowser()) { process.nextTick(() => { streaming.start() }) } else { setTimeout(() => { streaming.start() }, 0) } return streaming } return Client })() MisskeyAPI.Client = Client })(MisskeyAPI || (MisskeyAPI = {})) export default MisskeyAPI