Completed missing german translation with english replacements (help wanted!).

Some type tweaks to deal with TypeScript 5 requirements.
This commit is contained in:
elpamplina 2025-12-14 00:50:32 +01:00
parent 495a47df98
commit 7df2101754
3 changed files with 45 additions and 16 deletions

View file

@ -33,7 +33,7 @@ const encrypt = async (text: string, key: CryptoKey) => {
}
}
const pack = (buffer: ArrayBuffer) => {
const pack = (buffer: Uint8Array<ArrayBuffer>) => {
return window.btoa(
String.fromCharCode.apply(null, new Uint8Array(buffer))
)
@ -53,16 +53,16 @@ const unpack = (packed: string) => {
return string_to_buffer(window.atob(packed));
}
const decode = (bytestream: Uint8Array) => {
const decode = (bytestream: Uint8Array<ArrayBuffer>) => {
const decoder = new TextDecoder()
return decoder.decode(bytestream)
}
const decrypt = async (cipher: ArrayBuffer, key: CryptoKey, iv: ArrayBuffer) => {
const encoded = await window.crypto.subtle.decrypt({
const encoded = new Uint8Array(await window.crypto.subtle.decrypt({
name: 'AES-GCM',
iv: iv,
}, key, cipher)
}, key, cipher))
return decode(encoded)
}
@ -71,7 +71,7 @@ const generateKey = (seed: string) => window.crypto.subtle.digest('SHA-256', str
const encryptText = async (key: ArrayBuffer, text: string) => {
const cryptoKey = await importKey(key);
const {cipher, iv} = await encrypt(text, cryptoKey)
return pack(iv) + ':' + pack(cipher);
return pack(iv) + ':' + pack(new Uint8Array(cipher));
}
const decryptText = async (key: ArrayBuffer, text: string) => {

View file

@ -9,6 +9,8 @@
"connect_to": "Mit Mastodon verbinden",
"connect": "Verbinden",
"error": "Fehler beim Verbinden mit dem Server!",
"connecting": "Connecting to {{server}}...",
"cancel": "Cancel",
"max_post": "Zeichenbegrenzung",
"max_post_desc": "{{server}} erlaubt Beiträge mit {{max}} Zeichen.",
"visibility_first": "Sichtbarkeit des ersten Beitrags",
@ -24,7 +26,18 @@
"unlisted": "Öffentlich (still) (ungelistet und nicht im Such-Algorithmus)",
"private": "Follower (nur Personen, die dir folgen)"
},
"default_language": "Standardpostsprache"
"default_language": "Standardpostsprache",
"quote_approval": "Who can quote",
"quote_approval_desc": "Decide who can quote your posts",
"quote": {
"default": "As set on your account",
"public": "Everybody",
"followers": "Your followers only",
"nobody": "Nobody"
},
"quote_links": "Convert links into quotes",
"quote_links_desc": "Links to Mastodon posts will be converted into citations whenever possible.",
"quotes_not_supported": "The server doesn't support quotes."
},
"modal": {
"send_thread_count": "Versende einen Thread an Mastodon mit {{count}} Beiträgen",

36
main.ts
View file

@ -31,11 +31,23 @@ import {
import mime from "mime/lite";
import {decryptText, encryptText, generateKey} from "./encrypt";
import {QuoteApprovalPolicy} from "masto/dist/esm/mastodon/rest/v1/statuses";
import {StatusVisibility} from "masto/dist/cjs/mastodon/entities/v1";
import {Status, StatusVisibility} from "masto/dist/cjs/mastodon/entities/v1";
import {HttpResponse} from "masto/dist/cjs/interfaces";
import {Client} from "undici-types";
// @ts-ignore
const t = i18next.getFixedT(null, 'plugin-mastodon-threading', null);
type SearchStatus = Status & {
quoteApproval: {
currentUser: 'automatic' | 'manual' | 'denied' | 'unknown'
}
}
type VerifyCredentials = Client & {
scopes: string[]
}
interface MastodonThreadingSettings {
server: string,
clientId: string,
@ -329,14 +341,15 @@ export default class MastodonThreading extends Plugin {
const found_urls = post.text.match(pattern_url);
if (found_urls) {
// Iterate over URLs, searching for mastodon posts
for (let url: string of found_urls) {
for (let url of found_urls) {
const res = await this.getClient().v2.search.list({
q: url, type: 'statuses', resolve: true
});
requests++;
if (res.statuses.length === 1 &&
(res.statuses[0].quoteApproval.currentUser === 'automatic' ||
res.statuses[0].quoteApproval.currentUser === 'manual')) {
const statuses: SearchStatus[] = res.statuses as SearchStatus[];
if (statuses.length === 1 &&
(statuses[0].quoteApproval.currentUser === 'automatic' ||
statuses[0].quoteApproval.currentUser === 'manual')) {
post.quote = res.statuses[0].id;
post.text = post.text.replace(url, '');
break; // Only one quote per post
@ -371,7 +384,9 @@ export default class MastodonThreading extends Plugin {
description: img.alt
});
img.mediaId = m.id;
if (parseInt(headers.get('x-ratelimit-remaining'), 10) < imageList.length - i) {
const ratelimit = headers.get('x-ratelimit-remaining');
if (ratelimit != null &&
parseInt(ratelimit, 10) < imageList.length - i) {
new Notice(t('error.rate_limit'));
return;
}
@ -388,7 +403,7 @@ export default class MastodonThreading extends Plugin {
media.push(img.mediaId);
}
}
let {data: status, headers} = await this.getClient().v1.statuses.create.$raw({
const resp: HttpResponse<Status> = await this.getClient().v1.statuses.create.$raw({
status: p.text,
spoilerText: p.warning,
visibility: (first ? visibility_first : visibility_rest),
@ -398,9 +413,10 @@ export default class MastodonThreading extends Plugin {
quotedStatusId: p.quote,
quoteApprovalPolicy: this.settings.quoteApproval === 'default'? null: this.settings.quoteApproval,
});
id_link = status.id;
id_link = resp.data.id;
first = false;
if (parseInt(headers.get('x-ratelimit-remaining'), 10) < posts.length - i) {
const ratelimit = resp.headers.get('x-ratelimit-remaining');
if (ratelimit != null && parseInt(ratelimit, 10) < posts.length - i) {
new Notice(t('error.rate_limit'));
return;
}
@ -502,7 +518,7 @@ export default class MastodonThreading extends Plugin {
async checkCredentials(): Promise<boolean> {
const client = this.getClient();
const app = await client.v1.apps.verifyCredentials();
const app: VerifyCredentials = await client.v1.apps.verifyCredentials() as unknown as VerifyCredentials;
return (app.scopes.includes('read:search') &&
app.scopes.includes('write:media') &&
app.scopes.includes('write:statuses'));