Merge branch 'develop' into feat/scylladb

This commit is contained in:
ThatOneCalculator 2023-10-16 18:25:02 -07:00
commit 9955430ff9
No known key found for this signature in database
GPG key ID: 8703CACD01000000
119 changed files with 261 additions and 10069 deletions

3
.gitignore vendored
View file

@ -57,9 +57,6 @@ packages/backend/assets/LICENSE
!/packages/backend/src/db
!/packages/backend/src/server/api/endpoints/drive/files
packages/megalodon/lib
packages/megalodon/.idea
# blender backups
*.blend1
*.blend2

View file

@ -28,7 +28,6 @@ COPY packages/backend/package.json packages/backend/package.json
COPY packages/client/package.json packages/client/package.json
COPY packages/sw/package.json packages/sw/package.json
COPY packages/firefish-js/package.json packages/firefish-js/package.json
COPY packages/megalodon/package.json packages/megalodon/package.json
COPY packages/backend/native-utils/package.json packages/backend/native-utils/package.json
COPY packages/backend/native-utils/npm/linux-x64-musl/package.json packages/backend/native-utils/npm/linux-x64-musl/package.json
COPY packages/backend/native-utils/npm/linux-arm64-musl/package.json packages/backend/native-utils/npm/linux-arm64-musl/package.json
@ -58,8 +57,6 @@ RUN apt-get update && apt-get install -y libvips-dev zip unzip tini ffmpeg
COPY . ./
COPY --from=build /firefish/packages/megalodon /firefish/packages/megalodon
# Copy node modules
COPY --from=build /firefish/node_modules /firefish/node_modules
COPY --from=build /firefish/packages/backend/node_modules /firefish/packages/backend/node_modules

View file

@ -217,8 +217,7 @@ id: 'aid'
#maxCaptionLength: 1500
# Reserved usernames that only the administrator can register with
reservedUsernames:
{{ .Values.firefish.reservedUsernames | toYaml }}
reservedUsernames: {{ .Values.firefish.reservedUsernames | toJson }}
# Whether disable HSTS
#disableHsts: true
@ -265,8 +264,7 @@ reservedUsernames:
# Proxy remote files (default: false)
#proxyRemoteFiles: true
allowedPrivateNetworks:
{{ .Values.firefish.allowedPrivateNetworks | toYaml }}
allowedPrivateNetworks: {{ .Values.firefish.allowedPrivateNetworks | toJson }}
# TWA
#twa:

View file

@ -646,7 +646,7 @@ emptyToDisableSmtpAuth: "Laisser le nom dutilisateur et le mot de passe vides
smtpSecure: "Utiliser SSL/TLS implicitement dans les connexions SMTP"
smtpSecureInfo: "Désactiver cette option lorsque STARTTLS est utilisé"
testEmail: "Tester la distribution de courriel"
wordMute: "Filtre de mots"
wordMute: "Filtre de mots et langages"
regexpError: "Erreur dexpression régulière"
instanceMute: "Serveur masqué"
userSaysSomething: "{name} a dit quelque chose"
@ -960,7 +960,8 @@ _accountDelete:
inProgress: "Suppression en cours"
_ad:
back: "Retour"
reduceFrequencyOfThisAd: "Voir cette publicité moins souvent"
reduceFrequencyOfThisAd: "Voir cette bannière moins souvent"
adsBy: Bannière communautaire par {by}
_forgotPassword:
enterEmail: "Entrez ici l'adresse e-mail que vous avez enregistrée pour votre compte.
Un lien vous permettant de réinitialiser votre mot de passe sera envoyé à cette
@ -1145,6 +1146,13 @@ _wordMute:
soft: "Doux"
hard: "Strict"
mutedNotes: "Publications masquées"
muteLangsDescription2: Utiliser les code de langage (i.e en, fr, ja, zh).
lang: Langage
langDescription: Cacher du fil de publication les publications qui correspondent
à ces langues.
muteLangs: Langages filtrés
muteLangsDescription: Séparer avec des espaces or des retours à la ligne pour une
condition OU (OR).
_instanceMute:
instanceMuteDescription2: "Séparer avec des sauts de lignes"
title: "Masque les publications provenant des serveurs listés."
@ -2218,3 +2226,5 @@ openServerInfo: Afficher les informations du serveur en cliquant sur le bandeau
serveur dune publication
indexable: Indexable
languageForTranslation: Langage post-traduction
vibrate: Jouer les vibrations
clickToShowPatterns: Cliquer pour montrer les patrons de modules

View file

@ -1 +1 @@
{}
_lang_: "हिन्दी"

View file

@ -1269,8 +1269,8 @@ _tutorial:
{introduction} atau \"Halo dunia!\" yang sederhana."
step5_1: "Linimasa, linimasa di mana-mana!"
step5_2: "Servermu memiliki {timelines} lini masa berbeda yang diaktifkan."
step5_3: "Lini masa Beranda {icon} adalah tempat di mana kamu bisa melihat postingan
dari akun yang kamu ikuti."
step5_3: "Lini masa Beranda {icon} adalah tempat kamu bisa melihat postingan dari
akun yang kamu ikuti."
step5_4: "Linimasa Lokal {icon} adalah tempat kamu dapat melihat postingan dari
siapa pun di server ini."
step6_1: "Jadi, tempat apa ini?"

View file

@ -615,7 +615,7 @@ emptyToDisableSmtpAuth: "ユーザー名とパスワードを空欄にするこ
smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
smtpSecureInfo: "STARTTLS使用時はオフにします。"
testEmail: "配信テスト"
wordMute: "ワードミュート"
wordMute: "単語または言語のミュート"
regexpError: "正規表現エラー"
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:"
instanceMute: "サーバーミュート"
@ -693,7 +693,7 @@ no: "いいえ"
driveFilesCount: "ドライブのファイル数"
driveUsage: "ドライブ使用量"
noCrawle: "クローラーによるインデックスを拒否"
noCrawleDescription: "検索エンジンにあなたのプロフィールや投稿、ページなどのコンテンツを登録(インデックス)しないよう要請します。"
noCrawleDescription: "Web検索にあなたのプロフィールや投稿、ページなどのコンテンツを登録(インデックス)しないよう要請します。"
lockedAccountInfo: "フォローを承認制にしても、投稿の公開範囲を「フォロワー」にしない限り、誰でもあなたの投稿を見られます。"
alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にする"
loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
@ -810,7 +810,7 @@ instanceSecurity: "サーバーのセキュリティー"
secureModeInfo: "認証情報の無いリモートサーバーからのリクエストに応えません。"
privateMode: "非公開モード"
privateModeInfo: "有効にすると、許可したサーバーのみからリクエストを受け付けます。"
allowedInstances: "許可されたサーバー"
allowedInstances: "許可するサーバー"
allowedInstancesDescription: "許可したいサーバーのホストを改行で区切って設定します。非公開モードだけで有効です。"
previewNoteText: "本文をプレビュー"
customCss: "カスタムCSS"
@ -1504,7 +1504,7 @@ _profile:
youCanIncludeHashtags: "ハッシュタグを含められます。"
metadata: "追加情報"
metadataEdit: "追加情報を編集"
metadataDescription: "プロフィールに表として追加情報を表示できます。{a}タグまたは{l}タグを{rel}とともに追加すると、プロフィールのリンクを確認できます。"
metadataDescription: "プロフィールに追加情報を表示できます。プロフィールにリンクしたウェブサイトやSNSアカウントに<link {rel}>タグ(または{a}タグ)を追加すると、そのリンクを本人認証できます。"
metadataLabel: "ラベル"
metadataContent: "内容"
changeAvatar: "アバター画像を変更"
@ -1990,3 +1990,6 @@ confirm: 確認
exportZip: ZIPをエクスポート
openServerInfo: "投稿内のサーバー名をクリックでサーバー情報を開く"
indexableDescription: MastodonやFirefishなどの検索機能に、あなたの投稿が表示されるのを許可します。
clickToShowPatterns: クリックしてトラックを表示
vibrate: 振動を有効にする
indexable: 投稿検索に登録

View file

@ -246,7 +246,7 @@ uploadFromUrl: "URLアップロード"
uploadFromUrlDescription: "このURLのファイルをアップロードしたいねん"
uploadFromUrlRequested: "アップロードしたい言うといたで"
uploadFromUrlMayTakeTime: "アップロード終わるんにちょい時間かかるかもしれへんわ。"
explore: "みける"
explore: "みける"
messageRead: "もう読まはった"
noMoreHistory: "これより過去の履歴はあらへんで"
startMessaging: "チャットやるで"
@ -334,7 +334,7 @@ bannerUrl: "バナー画像のURL"
backgroundImageUrl: "背景画像のURL"
basicInfo: "基本情報"
pinnedUsers: "ピン留めしたユーザー"
pinnedUsersDescription: "「みつける」ページとかにピン留めしたいユーザーをここに書けばええんやで。他ん人との名前は改行で区切ればええんやで。"
pinnedUsersDescription: "「みっける」ページとかにピン留めしときたい兄ちゃんらをここに書いといたらええわ。名前は改行で区切ればええで。"
pinnedPages: "ピン留めページ"
pinnedPagesDescription: "サーバーのいっちゃん上にピン留めしたいページのパスを、改行で区切って記述してな。"
pinnedClipId: "ピン留めするクリップのID"
@ -684,7 +684,7 @@ clips: "クリップ"
experimentalFeatures: "実験的機能やで"
developer: "開発者やで"
makeExplorable: "アカウントを見つけやすくするで"
makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らんくなるで。"
makeExplorableDescription: "オフにすると、「みっける」ページに名前が載らんくなるで。"
showGapBetweenNotesInTimeline: "タイムライン上の投稿を離して表示するで"
duplicate: "複製"
left: "左"
@ -872,7 +872,7 @@ _registry:
domain: "ドメイン"
createKey: "キーを作る"
_aboutFirefish:
about: "Firefishは、ThatOneCalculatorが2022年にMisskeyをいじって作った、オープンなソースのソフトウアーや。"
about: "Firefishは、ThatOneCalculatorが2022年にMisskeyをいじって作った、オープンなソースのソフトウアーや。"
contributors: "ごっつい貢献者"
allContributors: "全ての貢献者"
source: "ソースコード"
@ -1442,6 +1442,12 @@ _tutorial:
step1_2: 使い始める前に、いくつか設定を済ませまひょ。すぐできますえ。
step2_1: 最初に、あんさんのプロフィールを作りまひょ
step2_2: プロフィールを設定しはることで、他ん人があんさんの投稿を見たり、フォローしたりするときの助けになってます。
step3_2: "あんさんのホームとソーシャルタイムラインは、どなたはんをフォローしはるかで決まります。ほな、いくつかアカウントをフォローしてみまひょ。\n\
プロフィールの右上にある、まあるい+ボタンをクリックしはるとフォローできますえ。"
step4_1: 投稿しとーみ
step5_1: タイムライン! 文字と写真の宝石箱や~
step5_2: うちのサーバーでは{timelines}種類のタイムラインをご用意しとります。
step4_2: 最初は{introduction}に投稿したり、シンプルに「ここは賑やかどすなぁ。うちはそこまで喋れまへんが、どうぞよろしゅうに」などと投稿しはる方もいてます。
_postForm:
_placeholders:
b: なんかおましたか?

View file

@ -16,10 +16,10 @@ noNotifications: "Bildirim bulunmuyor"
settings: "Ayarlar"
basicSettings: "Temel Ayarlar"
otherSettings: "Diğer Ayarlar"
openInWindow: "Bir pencere ile aç"
openInWindow: "ılır pencerede aç"
profile: "Profil"
timeline: "Zaman çizelgesi"
noAccountDescription: "Bu kullanıcı henüz kendi hakkında kısmını yazmadı."
timeline: "Akış"
noAccountDescription: "Bu kullanıcı henüz \"hakkında\" kısmını yazmadı."
login: "Giriş Yap"
logout: ıkış Yap"
signup: "Kayıt Ol"
@ -29,7 +29,7 @@ addUser: "Kullanıcı Ekle"
favorite: "Favorilere ekle"
favorites: "Favoriler"
unfavorite: "Favorilerden Kaldır"
favorited: "Favorilerime eklendi."
favorited: "Favorilere eklendi."
alreadyFavorited: "Zaten favorilerinizde kayıtlı."
pin: "Sabitlenmiş"
unpin: "Sabitlemeyi kaldır"
@ -41,9 +41,9 @@ deleteAndEditConfirm: "Bu gönderiyi silip yeniden düzenlemek istiyor musunuz?
ilişkin tüm tepkiler, destekler ve yanıtlar silinecektir."
addToList: "Listeye ekle"
sendMessage: "Mesaj Gönder"
copyUsername: "Kullanıcı Adını Kopyala"
copyUsername: "Kullanıcı Adını kopyala"
searchUser: "Kullanıcıları ara"
pinned: "Sabitlenmiş"
pinned: "Profile sabitle"
remove: "Sil"
smtpUser: "Kullanıcı Adı"
smtpPass: "Şifre"
@ -240,7 +240,7 @@ instance: Sunucu
fetchingAsApObject: Fediverse'den çekiliyor
removeReaction: Tepkini sil
rememberNoteVisibility: Gönderi görünürlüğü ayarlarını hatırla
attachCancel: Eklentiyi kaldır
attachCancel: Ek'i kaldır
suspend: Askıya Al
unsuspend: Askıya Almayı Kaldır
unmute: Susturmayı Kaldır
@ -248,13 +248,13 @@ blockConfirm: Bu hesabı engellemek istediğinize emin misiniz?
unblockConfirm: Bu hesabın engelini kaldırmak istediğinize emin misiniz?
settingGuide: Tavsiye edilen ayarlar
cacheRemoteFilesDescription: Bu ayar devre dışı bırakıldığında, uzak dosyalar doğrudan
uzak sunucudan yüklenir. Bunun devre dışı bırakılması depolama kullanımını azaltacak,
ancak küçük resimler oluşturulmayacağından trafiği artıracaktır.
dosyanın bulunduğu sunucudan yüklenir. Bunun devre dışı bırakılması depolama kullanımını
azaltacak, ancak küçük resimler oluşturulmayacağından trafiği artıracaktır.
flagAsCatDescription: Kedi kulaklarına sahip olacak ve bir kedi gibi konuşacaksın!
flagSpeakAsCat: Kedi gibi konuş
setWallpaper: Arkaplanı ayarla
removeWallpaper: Arkaplanı sil
operations: Operasyonlar
operations: İşlemler
clearCachedFiles: Ön belleği temizle
clearCachedFilesConfirm: Önbelleğe alınan tüm uzak dosyaları silmek istediğinizden
emin misiniz?
@ -357,13 +357,13 @@ whatIsNew: Değişiklikleri göster
translate: Çevir
breakFollow: Takipçiyi sil
breakFollowConfirm: Takipçiyi kaldırmak istediğinizden emin misiniz?
unfollowConfirm: "{name}'i takibi bırakmak istediğinizden emin misiniz?"
unfollowConfirm: "{name} kullanıcısını takip etmeyi bırakmak istediğinizden emin misiniz?"
importRequested: Bir içe aktarma isteğinde bulundunuz. Bu biraz zaman alabilir.
somethingHappened: Bir hata ile karşılaşıldı
retry: Tekrar Dene
youShouldUpgradeClient: Bu sayfayı görüntülemek için, lütfen istemcinizi yenileyin.
reactionSetting: Tepki seçicide gösterilecek tepkiler
unmarkAsSensitive: NSFW işaretini kaldır
unmarkAsSensitive: NSFW (Müstehcen İçerik) işaretini kaldır
enterFileName: Dosya adı gir
noJobs: Hiçbir iş yok
instanceFollowing: Sunucuda takip ediliyor
@ -481,8 +481,8 @@ mention: Bahset
download: İndir
lists: Listeler
noLists: Hiç listen yok
cantRenote: Bu gönderi yükseltilemez.
cantReRenote: Bir yükseltme tekrar yükseltilemez.
cantRenote: Bu gönderi desteklenemez.
cantReRenote: Bir destek tekrardan desteklenemez.
mute: Sustur
block: Engelle
editWidgetsExit: Tamamlandı
@ -636,10 +636,10 @@ reactionSettingDescription2: Yeniden sıralamak için sürükleyin, silmek için
eklemek için "+"ya basın.
you: Sen
clickToShow: Görmek için tıkla
sensitive: NSFW
sensitive: NSFW (Müstehcen İçerik)
add: Ekle
reaction: Tepkiler
markAsSensitive: NSFW olarak işaretle
markAsSensitive: NSFW (Müstehcen İçerik) olarak işaretle
unblock: Engeli Kaldır
addAccount: Hesap ekle
network: İnternet
@ -722,11 +722,11 @@ moveAccountDescription: Bu süreç geri döndürülemez. Taşımadan önce yeni
şeklinde biçimlendirilmiş hesabın etiketini girin
emojis: Emoji
flagAsCat: Kedi misin? 😺
selectChannel: Kanal seç
selectChannel: Bir kanal seç
emojiName: Emoji adı
showOnRemote: Orijinal sayfayı
flagSpeakAsCatDescription: Gönderileriniz kedi modundayken miyavdirilecektir
flagShowTimelineReplies: Yanıtları zaman çizelgesinde göster
flagShowTimelineReplies: Yanıtları akışta göster
silenceThisInstance: Bu sunucuyu sustur
proxyAccountDescription: Vekil hesabı, belirli koşullar altında kullanıcılar için
uzaktan takipçi işlevi gören bir hesaptır. Örneğin, bir kullanıcı listeye bir uzak
@ -845,8 +845,8 @@ pageLoadErrorDescription: Bu problem genelde ağ hataları veya tarayıcının
kaynaklanır. Önbelleği temizlemeyi deneyin ve biraz bekledikten sonra tekrar deneyin.
quote: Alıntıla
pinnedNote: Sabitlenmiş gönderi
renote: Yükselt
unrenote: Yükseltmeyi geri al
renote: Destekle
unrenote: Desteklemeyi geri al
emojiUrl: Emoji URL'si
suspendConfirm: Bu hesabı askıya almak istediğinize emin misiniz?
addEmoji: Ekle
@ -858,7 +858,7 @@ wallpaper: Arkaplan
searchWith: 'Arat: {q}'
youHaveNoLists: Hiçbir listen yok
followConfirm: '{name} kullanıcısını takip etmek istediğine emin misin?'
metadata: Metadata
metadata: Üstveri
monitor: Monitör
jobQueue: İş Sırası
noUsers: Kullanıcılar bulunamadı
@ -1013,7 +1013,7 @@ incorrectPassword: Yanlış şifre.
voteConfirm: '"{choice}" için oyunuzu onaylıyor musunuz?'
failedToFetchAccountInformation: Hesap bilgileri getirilemedi
rateLimitExceeded: Hız limiti aşıldı
renotedBy: '{user} Yükseltti'
renotedBy: '{user} destekledi'
host: Host
objectStorage: Nesne Depolaması
objectStorageUseSSLDesc: API bağlantıları için HTTPS kullanmayacaksanız bunu kapatın
@ -1026,8 +1026,8 @@ verificationEmailSent: Bir doğrulama maili gönderildi. Doğrulamayı tamamlama
lütfen verilen bağlantıyı takip edin.
hashtags: Etiketler
resolved: Çözüldü
flagShowTimelineRepliesDescription: ıksa, kullanıcıların zaman çizelgesindeki diğer
kullanıcıların gönderilerine verdiği yanıtları gösterir.
flagShowTimelineRepliesDescription: ıksa, kullanıcıların akıştaki diğer kullanıcıların
gönderilerine verdiği yanıtları gösterir.
clearQueueConfirmText: Kuyrukta kalan teslim edilmemiş gönderiler birleştirilmeyecektir.
Genellikle bu işleme gerek yoktur.
image: Resim
@ -1040,8 +1040,8 @@ unsuspendConfirm: Bu hesabın askıya almasını kaldırmak istediğinize emin m
selectList: Liste seç
editWidgets: Widget'ları düzenle
showEmojisInReactionNotifications: Tepki bildirimlerinde emojileri göster
renoteMute: Yükseltmeleri sustur
renoteUnmute: Yükseltmeleri susturmayı kaldır
renoteMute: Desteklemeleri sustur
renoteUnmute: Desteklemelerde ki susturmayı kaldır
loginFailed: Giriş yapılamadı
proxyAccount: Vekil Hesap
selectUser: Kullanıcı seç
@ -1068,7 +1068,7 @@ hideThisNote: Bu gönderiyi gizle
file: Dosya
enableEmojiReactions: Emoji tepkilerini aç
cw: İçerik uyarısı
makeFollowManuallyApprove: Onay gerektiren takip istekleri
makeFollowManuallyApprove: Onayınızı gerektiren takip istekleri
today: Bugün
enableRecommendedTimeline: Tavsiye edilen zaman çizgisini aktive et
state: Durum
@ -1165,7 +1165,7 @@ indexFromDescription: Her gönderiyi dizine eklemek için boş bırakın
indexNotice: Şimdi indeksleniyor. Bu muhtemelen biraz zaman alacaktır, lütfen sunucunuzu
en az bir saat yeniden başlatmayın.
customKaTeXMacro: Özel KaTeX makroları
directNotes: Direkt Mesajlar
directNotes: Özel Mesajlar
import: İçeri Aktar
export: Dışarı Aktar
mentions: Bahsetmeler
@ -1173,8 +1173,8 @@ files: Dosyalar
driveFileDeleteConfirm: '"{name}" dosyasını silmek istediğinizden emin misiniz? Dosyayı
"Ek" olarak içeren tüm gönderilerden kaldırılacaktır.'
createList: Liste oluştur
listsDesc: Listeler, belirtilen kullanıcılarla zaman çizelgesi oluşturmanıza olanak
tanır. Zaman Çizelgesi sayfasından erişilebilirler.
listsDesc: Listeler, belirtilen kullanıcıların içeriklerini içeren akışlar oluşturmanıza
olanak tanır. Akış sayfasından erişilebilirler.
note: Gönder
enterListName: Liste için isim gir
unfollow: Takipten Çık
@ -1183,14 +1183,14 @@ followRequestPending: Takip isteği bekleniyor
enterEmoji: Bir emoji gir
followRequest: Takip İsteği
followRequests: Takip istekleri
renoted: Yükseldi.
renoted: Desteklendi.
emoji: Emoji
cacheRemoteFiles: Uzak dosyaları önbelleğe al
flagAsBot: Bu hesabı robot olarak işaretle
flagAsBotDescription: Bu hesap bir program tarafından kontrol ediliyorsa bu seçeneği
etkinleştirin. Etkinleştirilirse, diğer geliştiricilerin botlarıyla sonsuz etkileşim
zincirlerinin önlemesi ve Firefish'in dahili sistemlerinin bu hesabı bir bot olarak
ele alacak şekilde ayarlaması için bir bayrak görevi görür.
ele alacak şekilde ayarlaması için bir işaret görevi görür.
clearQueue: Sırayı Temizle
hiddenTags: Gizlenmiş Etiketler
done: Tamamlandı
@ -2156,3 +2156,4 @@ importZip: ZIP içe aktar
indexable: Endekslenebilir
languageForTranslation: Çeviri sonrası dili
confirm: Onayla
clickToShowPatterns: Modülün örüntülerini göstermek için tıklayın

View file

@ -458,7 +458,7 @@ youHaveNoGroups: "找不到群組"
joinOrCreateGroup: "請加入現有群組,或創建新群組。"
noHistory: "沒有歷史紀錄"
signinHistory: "登入歷史"
disableAnimatedMfm: "用MFM動畫"
disableAnimatedMfm: "用MFM動畫"
doing: "正在處理..."
category: "類別"
tags: "標籤"
@ -596,7 +596,7 @@ emptyToDisableSmtpAuth: "留空使用者名稱及密碼以關閉SMTP驗證"
smtpSecure: "在 SMTP 連接中使用隱式 SSL/TLS"
smtpSecureInfo: "如使用STARTTLS請關閉"
testEmail: "測試郵件發送"
wordMute: "被靜音的文字"
wordMute: "被靜音的文字及語言"
regexpError: "正規表達式錯誤"
regexpErrorDescription: "{tab} 靜音文字的第 {line} 行的正規表達式有錯誤:"
instanceMute: "伺服器的靜音"
@ -765,7 +765,7 @@ user: "使用者"
administration: "管理"
accounts: "帳戶"
switch: "切換"
noMaintainerInformationWarning: "尚未設定管理員信息。"
noMaintainerInformationWarning: "尚未設定管理員資訊。"
noBotProtectionWarning: "尚未設定Bot防護。"
configure: "設定"
postToGallery: "發佈到相簿"
@ -899,7 +899,7 @@ customKaTeXMacro: "自訂KaTeX巨集"
customKaTeXMacroDescription: "使用巨集來輕鬆輸入數學表達式吧!巨集的用法與 LaTeX 中的命令定義相同。你可以使用 \\newcommand{\\
name}{content} 或 \\newcommand{\\name}[number of arguments]{content} 來輸入數學表達式。舉例來說,\\
newcommand{\\add}[2]{#1 + #2} 會將 \\add{3}{foo} 展開為 3 + foo。巨集名稱除了可用大括號 {} 括起來之外,也可使用小括號
() 和中括號 [],但使用於巨集參數的括號會有所變更。每行只能夠定義一個巨集,巨集中間無法間換。無效的行將被忽略。只支援簡單字串的替換功能,不支援條件分歧的高級語法。"
() 和中括號 [],但使用於巨集參數的括號會有所變更。每行只能夠定義一個巨集,巨集中間無法間換。無效的行將被忽略。只支援簡單字串的替換功能,不支援條件分歧的進階語法。"
enableCustomKaTeXMacro: "啟用自定義 KaTeX 宏"
_sensitiveMediaDetection:
description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。"
@ -932,7 +932,7 @@ _accountDelete:
inProgress: "正在刪除"
_ad:
back: "返回"
reduceFrequencyOfThisAd: "降低此廣告的頻率"
reduceFrequencyOfThisAd: "降低此橫幅的頻率"
_forgotPassword:
enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。"
ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。"
@ -1026,7 +1026,7 @@ _mfm:
emoji: "自訂表情符號"
emojiDescription: "您可以通過將自定義表情符號名稱括在冒號中來顯示自定義表情符號。"
search: "搜尋"
searchDescription: "您可以顯示所輸入的搜索框。"
searchDescription: "顯示含有指定文字的搜尋欄。"
flip: "翻轉"
flipDescription: "將內容上下或左右翻轉。"
jelly: "動畫(果凍)"
@ -1068,7 +1068,7 @@ _mfm:
alwaysPlay: 自動播放所有MFM動畫
positionDescription: 按指定數量移動內容。
advancedDescription: 如果停用僅顯示基礎MFM及正在播放的MFM動畫
advanced: 高級MFM
advanced: 進階MFM
fade: 淡出
foreground: 文字顏色
crop: 裁切
@ -1230,7 +1230,7 @@ _tutorial:
step3_1: "現在是時候追隨一些人了!"
step3_2: "你的主頁和社交時間線是基於你所追蹤的人,所以試著先追蹤幾個帳戶。\n點擊個人資料右上角的加號圈就可以關注它。"
step4_1: "讓我們出去找你。"
step4_2: "對於他們的第一條信息,有些人喜歡做 {introduction} 或一個簡單的 \"hello world!\""
step4_2: "作為第一則貼文,有些人喜歡發 {introduction} 或單純發一個 \"hello world!\""
step5_1: "時間線,到處都是時間線!"
step5_2: "您的伺服器已啟用了{timelines}個時間線。"
step5_3: "首頁 {icon} 時間線是顯示你追蹤的帳號的貼文。"
@ -1815,7 +1815,7 @@ _deck:
secureMode: 安全模式(授權獲取)
instanceSecurity: 伺服器安全性
privateMode: 私人模式
allowedInstances: 列入名單的伺服器
allowedInstances: 列入允許名單的伺服器
secureModeInfo: 當從其他伺服器請求時,不要在沒有證據的情況下發回。
_messaging:
dms: 私訊
@ -1823,8 +1823,8 @@ _messaging:
manageGroups: 管理群組
replayTutorial: 重新播放教程
moveFromLabel: '您想遷移的舊帳戶:'
customMOTDDescription: 每次用戶加載/重新加載頁面時,由換行符號分隔的 MOTD啟動畫面的自定信息將隨機顯示。
privateModeInfo: 啟用後,只有列入名單的伺服器才能與你的伺服器聯合。所有貼文都將對公眾隱藏。
customMOTDDescription: 自訂MOTD(啟動畫面)訊息,一行一個。每次用戶載入/重新整理頁面時將會隨機顯示。
privateModeInfo: 啟用後,只有列入允許名單的伺服器才能與你的伺服器聯合。所有貼文都將對公眾隱藏。
adminCustomCssWarn: 除非你知道它的作用,否則請不要使用此設定。 輸入不正確的值可能會導致每個人的客戶端無法正常運行。你可在你的的用戶設定中測試,確保你的
CSS 正常工作。
showUpdates: Firefish 更新時顯示彈出視窗
@ -1852,7 +1852,7 @@ enableEmojiReactions: 啟用表情符號反應
breakFollowConfirm: 您確定要移除該關注者嗎?
socialTimeline: 社交時間軸
cannotUploadBecauseExceedsFileSizeLimit: 因檔案太大而無法上傳。
customMOTD: 自定義MOTD (網頁載入時顯示的息)
customMOTD: 自定義MOTD (網頁載入時顯示的息)
customSplashIcons: 啟動畫面圖標 (網址)
splash: 啟動畫面
updateAvailable: 可能有可用的更新!
@ -1871,7 +1871,7 @@ _experiments:
title: 試驗功能
enablePostImports: 啟用匯入貼文的功能
findOtherInstance: 找找另一個伺服器
noGraze: 瀏覽器擴展 "Graze for Mastodon" 會與Firefish發生衝突請停用該擴展
noGraze: 瀏覽器擴充元件 "Graze for Mastodon" 會與Firefish發生衝突請停用該擴充元件
userSaysSomethingReasonRenote: '{name} 轉發了包含 {reason} 的貼文'
pushNotificationNotSupported: 你的瀏覽器或伺服器不支援推送通知
accessibility: 輔助功能
@ -1883,13 +1883,13 @@ deleted: 已刪除
editNote: 編輯貼文
edited: '於 {date} {time} 編輯'
userSaysSomethingReason: '{name} 說了 {reason}'
allowedInstancesDescription: 要加入聯邦白名單的服務器,每台伺服器用新行分隔(僅適用於私有模式)。
allowedInstancesDescription: 允許聯邦的伺服器名單,一行一個(僅適用於私人模式)。
defaultReaction: 默認的表情符號反應
license: 授權
apps: 應用
pushNotification: 推送通知
subscribePushNotification: 啟用推送通知
unsubscribePushNotification: 用推送通知
unsubscribePushNotification: 用推送通知
pushNotificationAlreadySubscribed: 推送通知已經啟用
recommendedInstancesDescription: 以每行分隔的推薦伺服器出現在推薦的時間線中。
searchPlaceholder: 在 Firefish 上搜尋
@ -1932,7 +1932,7 @@ isModerator: 板主
isAdmin: 管理員
isPatron: Firefish 項目贊助者
silencedWarning: 顯示此頁面是因為這些使用者來自您伺服器管理員已靜音的伺服器,因此他們可能是垃圾訊息。
signupsDisabled: 該伺服器上的註冊當前已被禁用,但您隨時可以在另一台伺服器上註冊!或是您有該伺服器的邀請碼,請在下面輸入。
signupsDisabled: 此伺服器目前停止註冊,但您隨時可以在另一台伺服器上註冊!如果您有此伺服器的邀請碼,請在下面輸入。
showPopup: 通過彈出式視窗通知用戶
showWithSparkles: 讓標題閃閃發光
youHaveUnreadAnnouncements: 您有未讀的公告

View file

@ -7,4 +7,3 @@ This directory contains all of the packages Firefish uses.
- `client`: Web interface written in Vue3 and TypeScript
- `sw`: Web [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) written in TypeScript
- `firefish-js`: TypeScript SDK for both backend and client, also published on [NPM](https://www.npmjs.com/package/firefish-js) for public use
- `megalodon`: TypeScript library used for partial Mastodon API compatibility

View file

@ -89,7 +89,7 @@
"koa-send": "5.0.1",
"koa-slow": "2.1.0",
"koa-views": "7.0.2",
"megalodon": "workspace:*",
"megalodon": "8.1.1",
"meilisearch": "0.34.1",
"mfm-js": "0.23.3",
"mime-types": "2.1.35",

View file

@ -24,7 +24,11 @@ export function getClient(
const accessTokenArr = authorization?.split(" ") ?? [null];
const accessToken = accessTokenArr[accessTokenArr.length - 1];
const generator = (megalodon as any).default;
const client = generator(BASE_URL, accessToken) as MegalodonInterface;
const client = generator(
"firefish",
BASE_URL,
accessToken,
) as MegalodonInterface;
return client;
}

View file

@ -68,7 +68,7 @@ export function apiAuthMastodon(router: Router): void {
website: body.website,
redirect_uri: red,
client_id: Buffer.from(appData.url || "").toString("base64"),
client_secret: appData.clientSecret,
client_secret: appData.client_secret,
};
console.log(returns);
ctx.body = returns;

View file

@ -1,8 +1,7 @@
import megalodon, { MegalodonInterface } from "megalodon";
import Router from "@koa/router";
import { getClient } from "../ApiMastodonCompatibleService.js";
import axios from "axios";
import { Converter } from "megalodon";
import Converter from "megalodon";
import { convertTimelinesArgsId, limitToInt } from "./timeline.js";
import { convertAccount, convertStatus } from "../converters.js";

View file

@ -380,7 +380,7 @@ export function apiStatusMastodon(router: Router): void {
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const data = await client.reactStatus(
const data = await client.createEmojiReaction(
convertId(ctx.params.id, IdType.FirefishId),
ctx.params.name,
);
@ -400,7 +400,7 @@ export function apiStatusMastodon(router: Router): void {
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const data = await client.unreactStatus(
const data = await client.deleteEmojiReaction(
convertId(ctx.params.id, IdType.FirefishId),
ctx.params.name,
);

View file

@ -25,7 +25,7 @@ import { readNotification } from "../common/read-notification.js";
import channels from "./channels/index.js";
import type Channel from "./channel.js";
import type { StreamEventEmitter, StreamMessages } from "./types.js";
import { Converter } from "megalodon";
import Converter from "megalodon";
import { getClient } from "../mastodon/ApiMastodonCompatibleService.js";
/**

View file

@ -179,11 +179,10 @@ mastoRouter.post("/oauth/token", async (ctx) => {
ctx.body = ret;
return;
}
let client_id: any = body.client_id;
let client_id: Array<string> | string | null = body.client_id;
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
const generator = (megalodon as any).default;
const client = generator(BASE_URL, null) as MegalodonInterface;
let m = null;
const client = generator("firefish", BASE_URL, null) as MegalodonInterface;
let token = null;
if (body.code) {
//m = body.code.match(/^([a-zA-Z0-9]{8})([a-zA-Z0-9]{4})([a-zA-Z0-9]{4})([a-zA-Z0-9]{4})([a-zA-Z0-9]{12})/);
@ -207,7 +206,7 @@ mastoRouter.post("/oauth/token", async (ctx) => {
token ? token : "",
);
const ret = {
access_token: atData.accessToken,
access_token: atData.access_token,
token_type: "Bearer",
scope: body.scope || "read write follow push",
created_at: Math.floor(new Date().getTime() / 1000),

View file

@ -24,7 +24,7 @@
@swiper="setSwiperRef"
@slide-change="onSlideChange"
>
<swiper-slide>
<swiper-slide v-if="true">
<MkFolder class="_gap">
<template #header
><i class="ph-clock ph-bold ph-lg"></i>
@ -66,7 +66,7 @@
</MkPagination>
</MkFolder>
</swiper-slide>
<swiper-slide>
<swiper-slide v-if="true">
<MkPagination
v-slot="{ items }"
:pagination="likedPostsPagination"
@ -81,7 +81,7 @@
</div>
</MkPagination>
</swiper-slide>
<swiper-slide>
<swiper-slide v-if="true">
<MkA to="/gallery/new" class="_link" style="margin: 16px"
><i class="ph-plus ph-bold ph-lg"></i>
{{ i18n.ts.postToGallery }}</MkA

View file

@ -66,6 +66,7 @@ const props = withDefaults(
},
},
);
props.value.children ??= [];
const getPageBlockList = inject<(any) => any>("getPageBlockList");

View file

@ -40,6 +40,7 @@ const props = withDefaults(
},
},
);
props.value.children ??= [];
const getPageBlockList = inject<(any) => any>("getPageBlockList");

View file

@ -23,7 +23,7 @@
@swiper="setSwiperRef"
@slide-change="onSlideChange"
>
<swiper-slide>
<swiper-slide v-if="true">
<div class="rknalgpo">
<MkPagination
v-slot="{ items }"
@ -38,7 +38,7 @@
</MkPagination>
</div>
</swiper-slide>
<swiper-slide>
<swiper-slide v-if="true">
<div class="rknalgpo liked">
<MkPagination
v-slot="{ items }"
@ -53,7 +53,7 @@
</MkPagination>
</div>
</swiper-slide>
<swiper-slide>
<swiper-slide v-if="true">
<div class="rknalgpo my">
<div class="buttoncontainer">
<MkButton class="new primary" @click="create()"

View file

@ -60,7 +60,7 @@ export default defineConfig(({ command, mode }) => {
),
_ENV_: JSON.stringify(process.env.NODE_ENV),
_DEV_: process.env.NODE_ENV !== "production",
_PERF_PREFIX_: JSON.stringify("Misskey:"),
_PERF_PREFIX_: JSON.stringify("Firefish:"),
_DATA_TRANSFER_DRIVE_FILE_: JSON.stringify("mk_drive_file"),
_DATA_TRANSFER_DRIVE_FOLDER_: JSON.stringify("mk_drive_folder"),
_DATA_TRANSFER_DECK_COLUMN_: JSON.stringify("mk_deck_column"),

View file

@ -1,83 +0,0 @@
{
"name": "megalodon",
"private": true,
"main": "./lib/src/index.js",
"typings": "./lib/src/index.d.ts",
"scripts": {
"build": "tsc -p ./",
"build:debug": "pnpm run build",
"lint": "pnpm biome check **/*.ts --apply",
"format": "pnpm biome format --write src/**/*.ts",
"doc": "typedoc --out ../docs ./src",
"test": "NODE_ENV=test jest -u --maxWorkers=3"
},
"jest": {
"moduleFileExtensions": [
"ts",
"js"
],
"moduleNameMapper": {
"^@/(.+)": "<rootDir>/src/$1",
"^~/(.+)": "<rootDir>/$1"
},
"testMatch": [
"**/test/**/*.spec.ts"
],
"preset": "ts-jest/presets/default",
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"globals": {
"ts-jest": {
"tsconfig": "tsconfig.json"
}
},
"testEnvironment": "node"
},
"dependencies": {
"@types/oauth": "^0.9.0",
"@types/ws": "^8.5.4",
"axios": "1.2.2",
"dayjs": "^1.11.7",
"form-data": "^4.0.0",
"https-proxy-agent": "^5.0.1",
"oauth": "^0.10.0",
"object-assign-deep": "^0.4.0",
"parse-link-header": "^2.0.0",
"socks-proxy-agent": "^7.0.0",
"typescript": "4.9.4",
"uuid": "^9.0.0",
"ws": "8.12.0",
"async-lock": "1.4.0"
},
"devDependencies": {
"@types/core-js": "^2.5.0",
"@types/form-data": "^2.5.0",
"@types/jest": "^29.4.0",
"@types/object-assign-deep": "^0.4.0",
"@types/parse-link-header": "^2.0.0",
"@types/uuid": "^9.0.0",
"@types/node": "18.11.18",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"@types/async-lock": "1.4.0",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-standard": "^5.0.0",
"jest": "^29.4.0",
"jest-worker": "^29.4.0",
"lodash": "^4.17.14",
"prettier": "^2.8.3",
"ts-jest": "^29.0.5",
"typedoc": "^0.23.24"
},
"directories": {
"lib": "lib",
"test": "test"
}
}

View file

@ -1 +0,0 @@
declare module "axios/lib/adapters/http";

View file

@ -1,13 +0,0 @@
export class RequestCanceledError extends Error {
public isCancel: boolean;
constructor(msg: string) {
super(msg);
this.isCancel = true;
Object.setPrototypeOf(this, RequestCanceledError);
}
}
export const isCancel = (value: any): boolean => {
return value && value.isCancel;
};

View file

@ -1,3 +0,0 @@
import MisskeyAPI from "./misskey/api_client";
export default MisskeyAPI.Converter;

View file

@ -1,3 +0,0 @@
export const NO_REDIRECT = "urn:ietf:wg:oauth:2.0:oob";
export const DEFAULT_SCOPE = ["read", "write", "follow"];
export const DEFAULT_UA = "megalodon";

View file

@ -1,27 +0,0 @@
/// <reference path="emoji.ts" />
/// <reference path="source.ts" />
/// <reference path="field.ts" />
namespace Entity {
export type Account = {
id: string;
username: string;
acct: string;
display_name: string;
locked: boolean;
created_at: string;
followers_count: number;
following_count: number;
statuses_count: number;
note: string;
url: string;
avatar: string;
avatar_static: string;
header: string;
header_static: string;
emojis: Array<Emoji>;
moved: Account | null;
fields: Array<Field>;
bot: boolean | null;
source?: Source;
};
}

View file

@ -1,8 +0,0 @@
namespace Entity {
export type Activity = {
week: string;
statuses: string;
logins: string;
registrations: string;
};
}

View file

@ -1,34 +0,0 @@
/// <reference path="tag.ts" />
/// <reference path="emoji.ts" />
/// <reference path="reaction.ts" />
namespace Entity {
export type Announcement = {
id: string;
content: string;
starts_at: string | null;
ends_at: string | null;
published: boolean;
all_day: boolean;
published_at: string;
updated_at: string;
read?: boolean;
mentions: Array<AnnouncementAccount>;
statuses: Array<AnnouncementStatus>;
tags: Array<Tag>;
emojis: Array<Emoji>;
reactions: Array<Reaction>;
};
export type AnnouncementAccount = {
id: string;
username: string;
url: string;
acct: string;
};
export type AnnouncementStatus = {
id: string;
url: string;
};
}

View file

@ -1,7 +0,0 @@
namespace Entity {
export type Application = {
name: string;
website?: string | null;
vapid_key?: string | null;
};
}

View file

@ -1,14 +0,0 @@
/// <reference path="attachment.ts" />
namespace Entity {
export type AsyncAttachment = {
id: string;
type: "unknown" | "image" | "gifv" | "video" | "audio";
url: string | null;
remote_url: string | null;
preview_url: string;
text_url: string | null;
meta: Meta | null;
description: string | null;
blurhash: string | null;
};
}

View file

@ -1,49 +0,0 @@
namespace Entity {
export type Sub = {
// For Image, Gifv, and Video
width?: number;
height?: number;
size?: string;
aspect?: number;
// For Gifv and Video
frame_rate?: string;
// For Audio, Gifv, and Video
duration?: number;
bitrate?: number;
};
export type Focus = {
x: number;
y: number;
};
export type Meta = {
original?: Sub;
small?: Sub;
focus?: Focus;
length?: string;
duration?: number;
fps?: number;
size?: string;
width?: number;
height?: number;
aspect?: number;
audio_encode?: string;
audio_bitrate?: string;
audio_channel?: string;
};
export type Attachment = {
id: string;
type: "unknown" | "image" | "gifv" | "video" | "audio";
url: string;
remote_url: string | null;
preview_url: string | null;
text_url: string | null;
meta: Meta | null;
description: string | null;
blurhash: string | null;
};
}

View file

@ -1,16 +0,0 @@
namespace Entity {
export type Card = {
url: string;
title: string;
description: string;
type: "link" | "photo" | "video" | "rich";
image?: string;
author_name?: string;
author_url?: string;
provider_name?: string;
provider_url?: string;
html?: string;
width?: number;
height?: number;
};
}

View file

@ -1,8 +0,0 @@
/// <reference path="status.ts" />
namespace Entity {
export type Context = {
ancestors: Array<Status>;
descendants: Array<Status>;
};
}

View file

@ -1,11 +0,0 @@
/// <reference path="account.ts" />
/// <reference path="status.ts" />
namespace Entity {
export type Conversation = {
id: string;
accounts: Array<Account>;
last_status: Status | null;
unread: boolean;
};
}

View file

@ -1,9 +0,0 @@
namespace Entity {
export type Emoji = {
shortcode: string;
static_url: string;
url: string;
visible_in_picker: boolean;
category: string;
};
}

View file

@ -1,8 +0,0 @@
namespace Entity {
export type FeaturedTag = {
id: string;
name: string;
statuses_count: number;
last_status_at: string;
};
}

View file

@ -1,7 +0,0 @@
namespace Entity {
export type Field = {
name: string;
value: string;
verified_at: string | null;
};
}

View file

@ -1,12 +0,0 @@
namespace Entity {
export type Filter = {
id: string;
phrase: string;
context: Array<FilterContext>;
expires_at: string | null;
irreversible: boolean;
whole_word: boolean;
};
export type FilterContext = string;
}

View file

@ -1,7 +0,0 @@
namespace Entity {
export type History = {
day: string;
uses: number;
accounts: number;
};
}

View file

@ -1,9 +0,0 @@
namespace Entity {
export type IdentityProof = {
provider: string;
provider_username: string;
updated_at: string;
proof_url: string;
profile_url: string;
};
}

View file

@ -1,41 +0,0 @@
/// <reference path="account.ts" />
/// <reference path="urls.ts" />
/// <reference path="stats.ts" />
namespace Entity {
export type Instance = {
uri: string;
title: string;
description: string;
email: string;
version: string;
thumbnail: string | null;
urls: URLs;
stats: Stats;
languages: Array<string>;
contact_account: Account | null;
max_toot_chars?: number;
registrations?: boolean;
configuration?: {
statuses: {
max_characters: number;
max_media_attachments: number;
characters_reserved_per_url: number;
};
media_attachments: {
supported_mime_types: Array<string>;
image_size_limit: number;
image_matrix_limit: number;
video_size_limit: number;
video_frame_limit: number;
video_matrix_limit: number;
};
polls: {
max_options: number;
max_characters_per_option: number;
min_expiration: number;
max_expiration: number;
};
};
};
}

View file

@ -1,6 +0,0 @@
namespace Entity {
export type List = {
id: string;
title: string;
};
}

View file

@ -1,15 +0,0 @@
namespace Entity {
export type Marker = {
home?: {
last_read_id: string;
version: number;
updated_at: string;
};
notifications?: {
last_read_id: string;
version: number;
updated_at: string;
unread_count?: number;
};
};
}

View file

@ -1,8 +0,0 @@
namespace Entity {
export type Mention = {
id: string;
username: string;
url: string;
acct: string;
};
}

View file

@ -1,15 +0,0 @@
/// <reference path="account.ts" />
/// <reference path="status.ts" />
namespace Entity {
export type Notification = {
account: Account;
created_at: string;
id: string;
status?: Status;
reaction?: Reaction;
type: NotificationType;
};
export type NotificationType = string;
}

View file

@ -1,14 +0,0 @@
/// <reference path="poll_option.ts" />
namespace Entity {
export type Poll = {
id: string;
expires_at: string | null;
expired: boolean;
multiple: boolean;
votes_count: number;
options: Array<PollOption>;
voted: boolean;
own_votes: Array<number>;
};
}

View file

@ -1,6 +0,0 @@
namespace Entity {
export type PollOption = {
title: string;
votes_count: number | null;
};
}

View file

@ -1,9 +0,0 @@
namespace Entity {
export type Preferences = {
"posting:default:visibility": "public" | "unlisted" | "private" | "direct";
"posting:default:sensitive": boolean;
"posting:default:language": string | null;
"reading:expand:media": "default" | "show_all" | "hide_all";
"reading:expand:spoilers": boolean;
};
}

View file

@ -1,16 +0,0 @@
namespace Entity {
export type Alerts = {
follow: boolean;
favourite: boolean;
mention: boolean;
reblog: boolean;
poll: boolean;
};
export type PushSubscription = {
id: string;
endpoint: string;
server_key: string;
alerts: Alerts;
};
}

View file

@ -1,12 +0,0 @@
/// <reference path="account.ts" />
namespace Entity {
export type Reaction = {
count: number;
me: boolean;
name: string;
url?: string;
static_url?: string;
accounts?: Array<Account>;
};
}

View file

@ -1,17 +0,0 @@
namespace Entity {
export type Relationship = {
id: string;
following: boolean;
followed_by: boolean;
delivery_following?: boolean;
blocking: boolean;
blocked_by: boolean;
muting: boolean;
muting_notifications: boolean;
requested: boolean;
domain_blocking: boolean;
showing_reblogs: boolean;
endorsed: boolean;
notifying: boolean;
};
}

View file

@ -1,9 +0,0 @@
namespace Entity {
export type Report = {
id: string;
action_taken: string;
comment: string;
account_id: string;
status_ids: Array<string>;
};
}

View file

@ -1,11 +0,0 @@
/// <reference path="account.ts" />
/// <reference path="status.ts" />
/// <reference path="tag.ts" />
namespace Entity {
export type Results = {
accounts: Array<Account>;
statuses: Array<Status>;
hashtags: Array<Tag>;
};
}

View file

@ -1,10 +0,0 @@
/// <reference path="attachment.ts" />
/// <reference path="status_params.ts" />
namespace Entity {
export type ScheduledStatus = {
id: string;
scheduled_at: string;
params: StatusParams;
media_attachments: Array<Attachment>;
};
}

View file

@ -1,10 +0,0 @@
/// <reference path="field.ts" />
namespace Entity {
export type Source = {
privacy: string | null;
sensitive: boolean | null;
language: string | null;
note: string;
fields: Array<Field>;
};
}

View file

@ -1,7 +0,0 @@
namespace Entity {
export type Stats = {
user_count: number;
status_count: number;
domain_count: number;
};
}

View file

@ -1,45 +0,0 @@
/// <reference path="account.ts" />
/// <reference path="application.ts" />
/// <reference path="mention.ts" />
/// <reference path="tag.ts" />
/// <reference path="attachment.ts" />
/// <reference path="emoji.ts" />
/// <reference path="card.ts" />
/// <reference path="poll.ts" />
/// <reference path="reaction.ts" />
namespace Entity {
export type Status = {
id: string;
uri: string;
url: string;
account: Account;
in_reply_to_id: string | null;
in_reply_to_account_id: string | null;
reblog: Status | null;
content: string;
plain_content: string | null;
created_at: string;
emojis: Emoji[];
replies_count: number;
reblogs_count: number;
favourites_count: number;
reblogged: boolean | null;
favourited: boolean | null;
muted: boolean | null;
sensitive: boolean;
spoiler_text: string;
visibility: "public" | "unlisted" | "private" | "direct";
media_attachments: Array<Attachment>;
mentions: Array<Mention>;
tags: Array<Tag>;
card: Card | null;
poll: Poll | null;
application: Application | null;
language: string | null;
pinned: boolean | null;
reactions: Array<Reaction>;
quote: Status | null;
bookmarked: boolean;
};
}

View file

@ -1,23 +0,0 @@
/// <reference path="account.ts" />
/// <reference path="application.ts" />
/// <reference path="mention.ts" />
/// <reference path="tag.ts" />
/// <reference path="attachment.ts" />
/// <reference path="emoji.ts" />
/// <reference path="card.ts" />
/// <reference path="poll.ts" />
/// <reference path="reaction.ts" />
namespace Entity {
export type StatusEdit = {
account: Account;
content: string;
plain_content: string | null;
created_at: string;
emojis: Emoji[];
sensitive: boolean;
spoiler_text: string;
media_attachments: Array<Attachment>;
poll: Poll | null;
};
}

View file

@ -1,12 +0,0 @@
namespace Entity {
export type StatusParams = {
text: string;
in_reply_to_id: string | null;
media_ids: Array<string> | null;
sensitive: boolean | null;
spoiler_text: string | null;
visibility: "public" | "unlisted" | "private" | "direct";
scheduled_at: string | null;
application_id: string;
};
}

View file

@ -1,10 +0,0 @@
/// <reference path="history.ts" />
namespace Entity {
export type Tag = {
name: string;
url: string;
history: Array<History> | null;
following?: boolean;
};
}

View file

@ -1,8 +0,0 @@
namespace Entity {
export type Token = {
access_token: string;
token_type: string;
scope: string;
created_at: number;
};
}

View file

@ -1,5 +0,0 @@
namespace Entity {
export type URLs = {
streaming_api: string;
};
}

View file

@ -1,38 +0,0 @@
/// <reference path="./entities/account.ts" />
/// <reference path="./entities/activity.ts" />
/// <reference path="./entities/announcement.ts" />
/// <reference path="./entities/application.ts" />
/// <reference path="./entities/async_attachment.ts" />
/// <reference path="./entities/attachment.ts" />
/// <reference path="./entities/card.ts" />
/// <reference path="./entities/context.ts" />
/// <reference path="./entities/conversation.ts" />
/// <reference path="./entities/emoji.ts" />
/// <reference path="./entities/featured_tag.ts" />
/// <reference path="./entities/field.ts" />
/// <reference path="./entities/filter.ts" />
/// <reference path="./entities/history.ts" />
/// <reference path="./entities/identity_proof.ts" />
/// <reference path="./entities/instance.ts" />
/// <reference path="./entities/list.ts" />
/// <reference path="./entities/marker.ts" />
/// <reference path="./entities/mention.ts" />
/// <reference path="./entities/notification.ts" />
/// <reference path="./entities/poll.ts" />
/// <reference path="./entities/poll_option.ts" />
/// <reference path="./entities/preferences.ts" />
/// <reference path="./entities/push_subscription.ts" />
/// <reference path="./entities/reaction.ts" />
/// <reference path="./entities/relationship.ts" />
/// <reference path="./entities/report.ts" />
/// <reference path="./entities/results.ts" />
/// <reference path="./entities/scheduled_status.ts" />
/// <reference path="./entities/source.ts" />
/// <reference path="./entities/stats.ts" />
/// <reference path="./entities/status.ts" />
/// <reference path="./entities/status_params.ts" />
/// <reference path="./entities/tag.ts" />
/// <reference path="./entities/token.ts" />
/// <reference path="./entities/urls.ts" />
export default Entity;

View file

@ -1,11 +0,0 @@
import Entity from "./entity";
namespace FilterContext {
export const Home: Entity.FilterContext = "home";
export const Notifications: Entity.FilterContext = "notifications";
export const Public: Entity.FilterContext = "public";
export const Thread: Entity.FilterContext = "thread";
export const Account: Entity.FilterContext = "account";
}
export default FilterContext;

View file

@ -1,32 +0,0 @@
import Response from "./response";
import OAuth from "./oauth";
import { isCancel, RequestCanceledError } from "./cancel";
import { ProxyConfig } from "./proxy_config";
import generator, {
detector,
MegalodonInterface,
WebSocketInterface,
} from "./megalodon";
import Misskey from "./misskey";
import Entity from "./entity";
import NotificationType from "./notification";
import FilterContext from "./filter_context";
import Converter from "./converter";
export {
Response,
OAuth,
RequestCanceledError,
isCancel,
ProxyConfig,
detector,
MegalodonInterface,
WebSocketInterface,
NotificationType,
FilterContext,
Misskey,
Entity,
Converter,
};
export default generator;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,727 +0,0 @@
import axios, { AxiosResponse, AxiosRequestConfig } from "axios";
import dayjs from "dayjs";
import FormData from "form-data";
import { DEFAULT_UA } from "../default";
import proxyAgent, { ProxyConfig } from "../proxy_config";
import Response from "../response";
import MisskeyEntity from "./entity";
import MegalodonEntity from "../entity";
import WebSocket from "./web_socket";
import MisskeyNotificationType from "./notification";
import NotificationType from "../notification";
namespace MisskeyAPI {
export namespace Entity {
export type App = MisskeyEntity.App;
export type Announcement = MisskeyEntity.Announcement;
export type Blocking = MisskeyEntity.Blocking;
export type Choice = MisskeyEntity.Choice;
export type CreatedNote = MisskeyEntity.CreatedNote;
export type Emoji = MisskeyEntity.Emoji;
export type Favorite = MisskeyEntity.Favorite;
export type Field = MisskeyEntity.Field;
export type File = MisskeyEntity.File;
export type Follower = MisskeyEntity.Follower;
export type Following = MisskeyEntity.Following;
export type FollowRequest = MisskeyEntity.FollowRequest;
export type Hashtag = MisskeyEntity.Hashtag;
export type List = MisskeyEntity.List;
export type Meta = MisskeyEntity.Meta;
export type Mute = MisskeyEntity.Mute;
export type Note = MisskeyEntity.Note;
export type Notification = MisskeyEntity.Notification;
export type Poll = MisskeyEntity.Poll;
export type Reaction = MisskeyEntity.Reaction;
export type Relation = MisskeyEntity.Relation;
export type User = MisskeyEntity.User;
export type UserDetail = MisskeyEntity.UserDetail;
export type UserDetailMe = MisskeyEntity.UserDetailMe;
export type GetAll = MisskeyEntity.GetAll;
export type UserKey = MisskeyEntity.UserKey;
export type Session = MisskeyEntity.Session;
export type Stats = MisskeyEntity.Stats;
export type State = MisskeyEntity.State;
export type APIEmoji = { emojis: Emoji[] };
}
export class Converter {
private baseUrl: string;
private instanceHost: string;
private plcUrl: string;
private 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: "plc",
avatar: "plc",
avatar_static: "plc",
header: "plc",
header_static: "plc",
followers_count: -1,
following_count: 0,
statuses_count: 0,
last_status_at: "1971-01-01T00:00:00.000Z",
noindex: true,
emojis: [],
fields: [],
moved: null,
};
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
this.instanceHost = baseUrl.substring(baseUrl.indexOf("//") + 2);
this.plcUrl = `${baseUrl}/static-assets/transparent.png`;
this.modelOfAcct.url = this.plcUrl;
this.modelOfAcct.avatar = this.plcUrl;
this.modelOfAcct.avatar_static = this.plcUrl;
this.modelOfAcct.header = this.plcUrl;
this.modelOfAcct.header_static = this.plcUrl;
}
// FIXME: Properly render MFM instead of just escaping HTML characters.
escapeMFM = (text: string): string =>
text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;")
.replace(/`/g, "&#x60;")
.replace(/\r?\n/g, "<br>");
emoji = (e: Entity.Emoji): MegalodonEntity.Emoji => {
return {
shortcode: e.name,
static_url: e.url,
url: e.url,
visible_in_picker: true,
category: e.category,
};
};
field = (f: Entity.Field): MegalodonEntity.Field => ({
name: f.name,
value: this.escapeMFM(f.value),
verified_at: null,
});
user = (u: Entity.User): MegalodonEntity.Account => {
let acct = u.username;
let acctUrl = `https://${u.host || this.instanceHost}/@${u.username}`;
if (u.host) {
acct = `${u.username}@${u.host}`;
acctUrl = `https://${u.host}/@${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: this.plcUrl,
header_static: this.plcUrl,
emojis: u.emojis.map((e) => this.emoji(e)),
moved: null,
fields: [],
bot: false,
};
};
userDetail = (
u: Entity.UserDetail,
host: string,
): MegalodonEntity.Account => {
let acct = u.username;
host = host.replace("https://", "");
let acctUrl = `https://${host || u.host || this.instanceHost}/@${
u.username
}`;
if (u.host) {
acct = `${u.username}@${u.host}`;
acctUrl = `https://${u.host}/@${u.username}`;
}
return {
id: u.id,
username: u.username,
acct: acct,
display_name: u.name || u.username,
locked: u.isLocked,
created_at: u.createdAt,
followers_count: u.followersCount,
following_count: u.followingCount,
statuses_count: u.notesCount,
note: u.description?.replace(/\n|\\n/g, "<br>") ?? "",
url: acctUrl,
avatar: u.avatarUrl,
avatar_static: u.avatarUrl,
header: u.bannerUrl ?? this.plcUrl,
header_static: u.bannerUrl ?? this.plcUrl,
emojis: u.emojis.map((e) => this.emoji(e)),
moved: null,
fields: u.fields.map((f) => this.field(f)),
bot: u.isBot,
};
};
userPreferences = (
u: MisskeyAPI.Entity.UserDetailMe,
v: "public" | "unlisted" | "private" | "direct",
): MegalodonEntity.Preferences => {
return {
"reading:expand:media": "default",
"reading:expand:spoilers": false,
"posting:default:language": u.lang,
"posting:default:sensitive": u.alwaysMarkNsfw,
"posting:default:visibility": v,
};
};
visibility = (
v: "public" | "home" | "followers" | "specified",
): "public" | "unlisted" | "private" | "direct" => {
switch (v) {
case "public":
return v;
case "home":
return "unlisted";
case "followers":
return "private";
case "specified":
return "direct";
}
};
encodeVisibility = (
v: "public" | "unlisted" | "private" | "direct",
): "public" | "home" | "followers" | "specified" => {
switch (v) {
case "public":
return v;
case "unlisted":
return "home";
case "private":
return "followers";
case "direct":
return "specified";
}
};
fileType = (
s: string,
): "unknown" | "image" | "gifv" | "video" | "audio" => {
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";
};
file = (f: Entity.File): MegalodonEntity.Attachment => {
return {
id: f.id,
type: this.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: f.comment,
blurhash: f.blurhash,
};
};
follower = (f: Entity.Follower): MegalodonEntity.Account => {
return this.user(f.follower);
};
following = (f: Entity.Following): MegalodonEntity.Account => {
return this.user(f.followee);
};
relation = (r: Entity.Relation): MegalodonEntity.Relationship => {
return {
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,
};
};
choice = (c: Entity.Choice): MegalodonEntity.PollOption => {
return {
title: c.text,
votes_count: c.votes,
};
};
poll = (p: Entity.Poll, id: string): MegalodonEntity.Poll => {
const now = dayjs();
const expire = dayjs(p.expiresAt);
const count = p.choices.reduce((sum, choice) => sum + choice.votes, 0);
return {
id: id,
expires_at: p.expiresAt,
expired: now.isAfter(expire),
multiple: p.multiple,
votes_count: count,
options: p.choices.map((c) => this.choice(c)),
voted: p.choices.some((c) => c.isVoted),
own_votes: p.choices
.filter((c) => c.isVoted)
.map((c) => p.choices.indexOf(c)),
};
};
note = (n: Entity.Note, host: string): MegalodonEntity.Status => {
host = host.replace("https://", "");
return {
id: n.id,
uri: n.uri ? n.uri : `https://${host}/notes/${n.id}`,
url: n.uri ? n.uri : `https://${host}/notes/${n.id}`,
account: this.user(n.user),
in_reply_to_id: n.replyId,
in_reply_to_account_id: n.reply?.userId ?? null,
reblog: n.renote ? this.note(n.renote, host) : null,
content: n.text ? this.escapeMFM(n.text) : "",
plain_content: n.text ? n.text : null,
created_at: n.createdAt,
// Remove reaction emojis with names containing @ from the emojis list.
emojis: n.emojis
.filter((e) => e.name.indexOf("@") === -1)
.map((e) => this.emoji(e)),
replies_count: n.repliesCount,
reblogs_count: n.renoteCount,
favourites_count: this.getTotalReactions(n.reactions),
reblogged: false,
favourited: !!n.myReaction,
muted: false,
sensitive: n.files ? n.files.some((f) => f.isSensitive) : false,
spoiler_text: n.cw ? n.cw : "",
visibility: this.visibility(n.visibility),
media_attachments: n.files ? n.files.map((f) => this.file(f)) : [],
mentions: [],
tags: [],
card: null,
poll: n.poll ? this.poll(n.poll, n.id) : null,
application: null,
language: null,
pinned: null,
// Use emojis list to provide URLs for emoji reactions.
reactions: this.mapReactions(n.emojis, n.reactions, n.myReaction),
bookmarked: false,
quote: n.renote && n.text ? this.note(n.renote, host) : null,
};
};
mapReactions = (
emojis: Array<MisskeyEntity.Emoji>,
r: { [key: string]: number },
myReaction?: string,
): Array<MegalodonEntity.Reaction> => {
// Map of emoji shortcodes to image URLs.
const emojiUrls = new Map<string, string>(
emojis.map((e) => [e.name, e.url]),
);
return Object.keys(r).map((key) => {
// Strip colons from custom emoji reaction names to match emoji shortcodes.
const shortcode = key.replaceAll(":", "");
// If this is a custom emoji (vs. a Unicode emoji), find its image URL.
const url = emojiUrls.get(shortcode);
// Finally, remove trailing @. from local custom emoji reaction names.
const name = shortcode.replace("@.", "");
return {
count: r[key],
me: key === myReaction,
name,
url,
// We don't actually have a static version of the asset, but clients expect one anyway.
static_url: url,
};
});
};
getTotalReactions = (r: { [key: string]: number }): number => {
return Object.values(r).length > 0
? Object.values(r).reduce(
(previousValue, currentValue) => previousValue + currentValue,
)
: 0;
};
reactions = (
r: Array<Entity.Reaction>,
): Array<MegalodonEntity.Reaction> => {
const result: Array<MegalodonEntity.Reaction> = [];
for (const e of r) {
const i = result.findIndex((res) => res.name === e.type);
if (i >= 0) {
result[i].count++;
} else {
result.push({
count: 1,
me: false,
name: e.type,
});
}
}
return result;
};
noteToConversation = (
n: Entity.Note,
host: string,
): MegalodonEntity.Conversation => {
const accounts: Array<MegalodonEntity.Account> = [this.user(n.user)];
if (n.reply) {
accounts.push(this.user(n.reply.user));
}
return {
id: n.id,
accounts: accounts,
last_status: this.note(n, host),
unread: false,
};
};
list = (l: Entity.List): MegalodonEntity.List => ({
id: l.id,
title: l.name,
});
encodeNotificationType = (
e: MegalodonEntity.NotificationType,
): MisskeyEntity.NotificationType => {
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.Poll:
return MisskeyNotificationType.PollEnded;
case NotificationType.FollowRequest:
return MisskeyNotificationType.ReceiveFollowRequest;
default:
return e;
}
};
decodeNotificationType = (
e: MisskeyEntity.NotificationType,
): MegalodonEntity.NotificationType => {
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.PollEnded:
return NotificationType.Poll;
case MisskeyNotificationType.ReceiveFollowRequest:
return NotificationType.FollowRequest;
case MisskeyNotificationType.FollowRequestAccepted:
return NotificationType.Follow;
default:
return e;
}
};
announcement = (a: Entity.Announcement): MegalodonEntity.Announcement => ({
id: a.id,
content: `<h1>${this.escapeMFM(a.title)}</h1>${this.escapeMFM(a.text)}`,
starts_at: null,
ends_at: null,
published: true,
all_day: false,
published_at: a.createdAt,
updated_at: a.updatedAt,
read: a.isRead,
mentions: [],
statuses: [],
tags: [],
emojis: [],
reactions: [],
});
notification = (
n: Entity.Notification,
host: string,
): MegalodonEntity.Notification => {
let notification = {
id: n.id,
account: n.user ? this.user(n.user) : this.modelOfAcct,
created_at: n.createdAt,
type: this.decodeNotificationType(n.type),
};
if (n.note) {
notification = Object.assign(notification, {
status: this.note(n.note, host),
});
if (notification.type === NotificationType.Poll) {
notification = Object.assign(notification, {
account: this.note(n.note, host).account,
});
}
if (n.reaction) {
notification = Object.assign(notification, {
reaction: this.mapReactions(n.note.emojis, { [n.reaction]: 1 })[0],
});
}
}
return notification;
};
stats = (s: Entity.Stats): MegalodonEntity.Stats => {
return {
user_count: s.usersCount,
status_count: s.notesCount,
domain_count: s.instances,
};
};
meta = (m: Entity.Meta, s: Entity.Stats): MegalodonEntity.Instance => {
const 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: `${wss}/streaming`,
},
stats: this.stats(s),
languages: m.langs,
contact_account: null,
max_toot_chars: m.maxNoteTextLength,
registrations: !m.disableRegistration,
};
};
hashtag = (h: Entity.Hashtag): MegalodonEntity.Tag => {
return {
name: h.tag,
url: h.tag,
history: null,
following: false,
};
};
}
export const 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",
];
/**
* Interface
*/
export interface Interface {
post<T = any>(
path: string,
params?: any,
headers?: { [key: string]: string },
): Promise<Response<T>>;
cancel(): void;
socket(
channel:
| "user"
| "localTimeline"
| "hybridTimeline"
| "globalTimeline"
| "conversation"
| "list",
listId?: string,
): WebSocket;
}
/**
* Misskey API client.
*
* Usign axios for request, you will handle promises.
*/
export class Client implements Interface {
private accessToken: string | null;
private baseUrl: string;
private userAgent: string;
private abortController: AbortController;
private proxyConfig: ProxyConfig | false = false;
private converter: Converter;
/**
* @param baseUrl hostname or base URL
* @param accessToken access token from OAuth2 authorization
* @param userAgent UserAgent is specified in header on request.
* @param proxyConfig Proxy setting, or set false if don't use proxy.
* @param converter Converter instance.
*/
constructor(
baseUrl: string,
accessToken: string | null,
userAgent: string = DEFAULT_UA,
proxyConfig: ProxyConfig | false = false,
converter: Converter,
) {
this.accessToken = accessToken;
this.baseUrl = baseUrl;
this.userAgent = userAgent;
this.proxyConfig = proxyConfig;
this.abortController = new AbortController();
this.converter = converter;
axios.defaults.signal = this.abortController.signal;
}
/**
* POST request to mastodon REST API.
* @param path relative path from baseUrl
* @param params Form data
* @param headers Request header object
*/
public async post<T>(
path: string,
params: any = {},
headers: { [key: string]: string } = {},
): Promise<Response<T>> {
let options: AxiosRequestConfig = {
headers: headers,
maxContentLength: Infinity,
maxBodyLength: Infinity,
};
if (this.proxyConfig) {
options = Object.assign(options, {
httpAgent: proxyAgent(this.proxyConfig),
httpsAgent: proxyAgent(this.proxyConfig),
});
}
let bodyParams = params;
if (this.accessToken) {
if (params instanceof FormData) {
bodyParams.append("i", this.accessToken);
} else {
bodyParams = Object.assign(params, {
i: this.accessToken,
});
}
}
return axios
.post<T>(this.baseUrl + path, bodyParams, options)
.then((resp: AxiosResponse<T>) => {
const res: Response<T> = {
data: resp.data,
status: resp.status,
statusText: resp.statusText,
headers: resp.headers,
};
return res;
});
}
/**
* Cancel all requests in this instance.
* @returns void
*/
public cancel(): void {
return this.abortController.abort();
}
/**
* Get connection and receive websocket connection for Misskey API.
*
* @param channel Channel name is user, localTimeline, hybridTimeline, globalTimeline, conversation or list.
* @param listId This parameter is required only list channel.
*/
public socket(
channel:
| "user"
| "localTimeline"
| "hybridTimeline"
| "globalTimeline"
| "conversation"
| "list",
listId?: string,
): WebSocket {
if (!this.accessToken) {
throw new Error("accessToken is required");
}
const url = `${this.baseUrl}/streaming`;
const streaming = new WebSocket(
url,
channel,
this.accessToken,
listId,
this.userAgent,
this.proxyConfig,
this.converter,
);
process.nextTick(() => {
streaming.start();
});
return streaming;
}
}
}
export default MisskeyAPI;

View file

@ -1,6 +0,0 @@
namespace MisskeyEntity {
export type GetAll = {
tutorial: number;
defaultNoteVisibility: "public" | "home" | "followers" | "specified";
};
}

View file

@ -1,10 +0,0 @@
namespace MisskeyEntity {
export type Announcement = {
id: string;
createdAt: string;
updatedAt: string;
text: string;
title: string;
isRead?: boolean;
};
}

View file

@ -1,9 +0,0 @@
namespace MisskeyEntity {
export type App = {
id: string;
name: string;
callbackUrl: string;
permission: Array<string>;
secret: string;
};
}

View file

@ -1,10 +0,0 @@
/// <reference path="userDetail.ts" />
namespace MisskeyEntity {
export type Blocking = {
id: string;
createdAt: string;
blockeeId: string;
blockee: UserDetail;
};
}

View file

@ -1,7 +0,0 @@
/// <reference path="note.ts" />
namespace MisskeyEntity {
export type CreatedNote = {
createdNote: Note;
};
}

View file

@ -1,9 +0,0 @@
namespace MisskeyEntity {
export type Emoji = {
name: string;
host: string | null;
url: string;
aliases: Array<string>;
category: string;
};
}

View file

@ -1,10 +0,0 @@
/// <reference path="note.ts" />
namespace MisskeyEntity {
export type Favorite = {
id: string;
createdAt: string;
noteId: string;
note: Note;
};
}

View file

@ -1,7 +0,0 @@
namespace MisskeyEntity {
export type Field = {
name: string;
value: string;
verified?: string;
};
}

View file

@ -1,20 +0,0 @@
namespace MisskeyEntity {
export type File = {
id: string;
createdAt: string;
name: string;
type: string;
md5: string;
size: number;
isSensitive: boolean;
properties: {
width: number;
height: number;
avgColor: string;
};
url: string;
thumbnailUrl: string;
comment: string;
blurhash: string;
};
}

View file

@ -1,9 +0,0 @@
/// <reference path="user.ts" />
namespace MisskeyEntity {
export type FollowRequest = {
id: string;
follower: User;
followee: User;
};
}

View file

@ -1,11 +0,0 @@
/// <reference path="userDetail.ts" />
namespace MisskeyEntity {
export type Follower = {
id: string;
createdAt: string;
followeeId: string;
followerId: string;
follower: UserDetail;
};
}

View file

@ -1,11 +0,0 @@
/// <reference path="userDetail.ts" />
namespace MisskeyEntity {
export type Following = {
id: string;
createdAt: string;
followeeId: string;
followerId: string;
followee: UserDetail;
};
}

View file

@ -1,7 +0,0 @@
namespace MisskeyEntity {
export type Hashtag = {
tag: string;
chart: Array<number>;
usersCount: number;
};
}

View file

@ -1,8 +0,0 @@
namespace MisskeyEntity {
export type List = {
id: string;
createdAt: string;
name: string;
userIds: Array<string>;
};
}

View file

@ -1,18 +0,0 @@
/// <reference path="emoji.ts" />
namespace MisskeyEntity {
export type Meta = {
maintainerName: string;
maintainerEmail: string;
name: string;
version: string;
uri: string;
description: string;
langs: Array<string>;
disableRegistration: boolean;
disableLocalTimeline: boolean;
bannerUrl: string;
maxNoteTextLength: 3000;
emojis: Array<Emoji>;
};
}

View file

@ -1,10 +0,0 @@
/// <reference path="userDetail.ts" />
namespace MisskeyEntity {
export type Mute = {
id: string;
createdAt: string;
muteeId: string;
mutee: UserDetail;
};
}

View file

@ -1,32 +0,0 @@
/// <reference path="user.ts" />
/// <reference path="emoji.ts" />
/// <reference path="file.ts" />
/// <reference path="poll.ts" />
namespace MisskeyEntity {
export type Note = {
id: string;
createdAt: string;
userId: string;
user: User;
text: string | null;
cw: string | null;
visibility: "public" | "home" | "followers" | "specified";
renoteCount: number;
repliesCount: number;
reactions: { [key: string]: number };
emojis: Array<Emoji>;
fileIds: Array<string>;
files: Array<File>;
replyId: string | null;
renoteId: string | null;
uri?: string;
reply?: Note;
renote?: Note;
viaMobile?: boolean;
tags?: Array<string>;
poll?: Poll;
mentions?: Array<string>;
myReaction?: string;
};
}

View file

@ -1,17 +0,0 @@
/// <reference path="user.ts" />
/// <reference path="note.ts" />
namespace MisskeyEntity {
export type Notification = {
id: string;
createdAt: string;
// https://github.com/syuilo/misskey/blob/056942391aee135eb6c77aaa63f6ed5741d701a6/src/models/entities/notification.ts#L50-L62
type: NotificationType;
userId: string;
user: User;
note?: Note;
reaction?: string;
};
export type NotificationType = string;
}

View file

@ -1,13 +0,0 @@
namespace MisskeyEntity {
export type Choice = {
text: string;
votes: number;
isVoted: boolean;
};
export type Poll = {
multiple: boolean;
expiresAt: string;
choices: Array<Choice>;
};
}

View file

@ -1,11 +0,0 @@
/// <reference path="user.ts" />
namespace MisskeyEntity {
export type Reaction = {
id: string;
createdAt: string;
user: User;
url?: string;
type: string;
};
}

View file

@ -1,12 +0,0 @@
namespace MisskeyEntity {
export type Relation = {
id: string;
isFollowing: boolean;
hasPendingFollowRequestFromYou: boolean;
hasPendingFollowRequestToYou: boolean;
isFollowed: boolean;
isBlocking: boolean;
isBlocked: boolean;
isMuted: boolean;
};
}

View file

@ -1,6 +0,0 @@
namespace MisskeyEntity {
export type Session = {
token: string;
url: string;
};
}

View file

@ -1,7 +0,0 @@
namespace MisskeyEntity {
export type State = {
isFavorited: boolean;
isMutedThread: boolean;
isWatching: boolean;
};
}

View file

@ -1,9 +0,0 @@
namespace MisskeyEntity {
export type Stats = {
notesCount: number;
originalNotesCount: number;
usersCount: number;
originalUsersCount: number;
instances: number;
};
}

View file

@ -1,13 +0,0 @@
/// <reference path="emoji.ts" />
namespace MisskeyEntity {
export type User = {
id: string;
name: string;
username: string;
host: string | null;
avatarUrl: string;
avatarColor: string;
emojis: Array<Emoji>;
};
}

View file

@ -1,34 +0,0 @@
/// <reference path="emoji.ts" />
/// <reference path="field.ts" />
/// <reference path="note.ts" />
namespace MisskeyEntity {
export type UserDetail = {
id: string;
name: string;
username: string;
host: string | null;
avatarUrl: string;
avatarColor: string;
isAdmin: boolean;
isModerator: boolean;
isBot: boolean;
isCat: boolean;
emojis: Array<Emoji>;
createdAt: string;
bannerUrl: string;
bannerColor: string;
isLocked: boolean;
isSilenced: boolean;
isSuspended: boolean;
description: string;
followersCount: number;
followingCount: number;
notesCount: number;
avatarId: string;
bannerId: string;
pinnedNoteIds?: Array<string>;
pinnedNotes?: Array<Note>;
fields: Array<Field>;
};
}

View file

@ -1,36 +0,0 @@
/// <reference path="emoji.ts" />
/// <reference path="field.ts" />
/// <reference path="note.ts" />
namespace MisskeyEntity {
export type UserDetailMe = {
id: string;
name: string;
username: string;
host: string | null;
avatarUrl: string;
avatarColor: string;
isAdmin: boolean;
isModerator: boolean;
isBot: boolean;
isCat: boolean;
emojis: Array<Emoji>;
createdAt: string;
bannerUrl: string;
bannerColor: string;
isLocked: boolean;
isSilenced: boolean;
isSuspended: boolean;
description: string;
followersCount: number;
followingCount: number;
notesCount: number;
avatarId: string;
bannerId: string;
pinnedNoteIds?: Array<string>;
pinnedNotes?: Array<Note>;
fields: Array<Field>;
alwaysMarkNsfw: boolean;
lang: string | null;
};
}

View file

@ -1,8 +0,0 @@
/// <reference path="user.ts" />
namespace MisskeyEntity {
export type UserKey = {
accessToken: string;
user: User;
};
}

View file

@ -1,28 +0,0 @@
/// <reference path="entities/app.ts" />
/// <reference path="entities/announcement.ts" />
/// <reference path="entities/blocking.ts" />
/// <reference path="entities/createdNote.ts" />
/// <reference path="entities/emoji.ts" />
/// <reference path="entities/favorite.ts" />
/// <reference path="entities/field.ts" />
/// <reference path="entities/file.ts" />
/// <reference path="entities/follower.ts" />
/// <reference path="entities/following.ts" />
/// <reference path="entities/followRequest.ts" />
/// <reference path="entities/hashtag.ts" />
/// <reference path="entities/list.ts" />
/// <reference path="entities/meta.ts" />
/// <reference path="entities/mute.ts" />
/// <reference path="entities/note.ts" />
/// <reference path="entities/notification.ts" />
/// <reference path="entities/poll.ts" />
/// <reference path="entities/reaction.ts" />
/// <reference path="entities/relation.ts" />
/// <reference path="entities/user.ts" />
/// <reference path="entities/userDetail.ts" />
/// <reference path="entities/userDetailMe.ts" />
/// <reference path="entities/userkey.ts" />
/// <reference path="entities/session.ts" />
/// <reference path="entities/stats.ts" />
export default MisskeyEntity;

Some files were not shown because too many files have changed in this diff Show more