@cutls/megalodon
Version:
Mastodon, Pleroma, Misskey API client for node.js and browser
722 lines (721 loc) • 19.3 kB
JavaScript
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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/`/g, '`').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