diff --git a/.config/dev.yml b/.config/dev.yml
new file mode 100644
index 0000000000..11c9d45d6a
--- /dev/null
+++ b/.config/dev.yml
@@ -0,0 +1,290 @@
+#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# Misskey configuration
+#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+# ┌─────┐
+#───┘ URL └─────────────────────────────────────────────────────
+
+# Final accessible URL seen by a user.
+url: http://misskey.local:3000
+
+# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
+# URL SETTINGS AFTER THAT!
+
+# ┌───────────────────────┐
+#───┘ Port and TLS settings └───────────────────────────────────
+
+#
+# Misskey requires a reverse proxy to support HTTPS connections.
+#
+# +----- https://example.tld/ ------------+
+# +------+ |+-------------+ +----------------+|
+# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
+# +------+ |+-------------+ +----------------+|
+# +---------------------------------------+
+#
+# You need to set up a reverse proxy. (e.g. nginx)
+# An encrypted connection with HTTPS is highly recommended
+# because tokens may be transferred in GET requests.
+
+# The port that your Misskey server should listen on.
+port: 3000
+
+# ┌──────────────────────────┐
+#───┘ PostgreSQL configuration └────────────────────────────────
+
+db:
+ host: postgres
+ port: 5432
+
+ # Database name
+ db: postgres
+
+ # Auth
+ user: postgres
+ pass: ci
+
+ # Whether disable Caching queries
+ #disableCache: true
+
+ # Extra Connection options
+ #extra:
+ # ssl: true
+
+dbReplications: false
+
+# You can configure any number of replicas here
+#dbSlaves:
+# -
+# host:
+# port:
+# db:
+# user:
+# pass:
+# -
+# host:
+# port:
+# db:
+# user:
+# pass:
+
+# ┌─────────────────────┐
+#───┘ Redis configuration └─────────────────────────────────────
+
+redis:
+ host: redis
+ port: 6379
+ #family: 0 # 0=Both, 4=IPv4, 6=IPv6
+ #pass: example-pass
+ #prefix: example-prefix
+ #db: 1
+
+#redisForPubsub:
+# host: redis
+# port: 6379
+# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
+# #pass: example-pass
+# #prefix: example-prefix
+# #db: 1
+
+#redisForJobQueue:
+# host: redis
+# port: 6379
+# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
+# #pass: example-pass
+# #prefix: example-prefix
+# #db: 1
+
+#redisForTimelines:
+# host: redis
+# port: 6379
+# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
+# #pass: example-pass
+# #prefix: example-prefix
+# #db: 1
+
+#redisForRateLimit:
+# host: localhost
+# port: 6379
+# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
+# #pass: example-pass
+# #prefix: example-prefix
+# #db: 1
+# # You can specify more ioredis options...
+# #username: example-username
+
+# ┌───────────────────────────────┐
+#───┘ Fulltext search configuration └─────────────────────────────
+
+# These are the setting items for the full-text search provider.
+fulltextSearch:
+ # You can select the ID generation method.
+ # - sqlLike (default)
+ # Use SQL-like search.
+ # This is a standard feature of PostgreSQL, so no special extensions are required.
+ # - sqlPgroonga
+ # Use pgroonga.
+ # You need to install pgroonga and configure it as a PostgreSQL extension.
+ # In addition to the above, you need to create a pgroonga index on the text column of the note table.
+ # see: https://pgroonga.github.io/tutorial/
+ # - meilisearch
+ # Use Meilisearch.
+ # You need to install Meilisearch and configure.
+ provider: sqlLike
+
+# For Meilisearch settings.
+# If you select "meilisearch" for "fulltextSearch.provider", it must be set.
+# You can set scope to local or global (default value)
+# (include notes from remote).
+
+#meilisearch:
+# host: meilisearch
+# port: 7700
+# apiKey: ''
+# ssl: true
+# index: ''
+# scope: global
+
+# ┌───────────────┐
+#───┘ ID generation └───────────────────────────────────────────
+
+# You can select the ID generation method.
+# You don't usually need to change this setting, but you can
+# change it according to your preferences.
+
+# Available methods:
+# aid ... Short, Millisecond accuracy
+# aidx ... Millisecond accuracy
+# meid ... Similar to ObjectID, Millisecond accuracy
+# ulid ... Millisecond accuracy
+# objectid ... This is left for backward compatibility
+
+# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
+# ID SETTINGS AFTER THAT!
+
+id: 'aidx'
+
+# ┌─────────────────────┐
+#───┘ Other configuration └─────────────────────────────────────
+
+# Whether disable HSTS
+#disableHsts: true
+
+# Number of worker processes
+#clusterLimit: 1
+
+# Job concurrency per worker
+# deliverJobConcurrency: 128
+# inboxJobConcurrency: 16
+# relashionshipJobConcurrency: 16
+# What's relashionshipJob?:
+# Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
+
+# Job rate limiter
+# deliverJobPerSec: 128
+# inboxJobPerSec: 32
+# relashionshipJobPerSec: 64
+
+# Job attempts
+# deliverJobMaxAttempts: 12
+# inboxJobMaxAttempts: 8
+
+# Local address used for outgoing requests
+#outgoingAddress: 127.0.0.1
+
+# IP address family used for outgoing request (ipv4, ipv6 or dual)
+#outgoingAddressFamily: ipv4
+
+# Amount of characters that can be used when writing notes. Longer notes will be rejected. (minimum: 1)
+#maxNoteLength: 3000
+# Amount of characters that will be saved for remote notes. Longer notes will be truncated to this length. (minimum: 1)
+#maxRemoteNoteLength: 100000
+# Amount of characters that can be used when writing content warnings. Longer warnings will be rejected. (minimum: 1)
+#maxCwLength: 500
+# Amount of characters that will be saved for remote content warnings. Longer warnings will be truncated to this length. (minimum: 1)
+#maxRemoteCwLength: 5000
+# Amount of characters that can be used when writing media descriptions (alt text). Longer descriptions will be rejected. (minimum: 1)
+#maxAltTextLength: 20000
+# Amount of characters that will be saved for remote media descriptions (alt text). Longer descriptions will be truncated to this length. (minimum: 1)
+#maxRemoteAltTextLength: 100000
+
+# Proxy for HTTP/HTTPS
+#proxy: http://127.0.0.1:3128
+
+proxyBypassHosts:
+ - api.deepl.com
+ - api-free.deepl.com
+ - www.recaptcha.net
+ - hcaptcha.com
+ - challenges.cloudflare.com
+
+# Proxy for SMTP/SMTPS
+#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
+#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
+#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
+
+# Media Proxy
+#mediaProxy: https://example.com/proxy
+
+# Proxy remote files (default: true)
+# Proxy remote files by this instance or mediaProxy to prevent remote files from running in remote domains.
+proxyRemoteFiles: true
+
+# Movie Thumbnail Generation URL
+# There is no reference implementation.
+# For example, Misskey will point to the following URL:
+# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
+#videoThumbnailGenerator: https://example.com
+
+# Sign outgoing ActivityPub GET request (default: true)
+signToActivityPubGet: true
+# Sign outgoing ActivityPub Activities (default: true)
+# Linked Data signatures are cryptographic signatures attached to each activity to provide proof of authenticity.
+# When using authorized fetch, this is often undesired as any signed activity can be forwarded to a blocked instance by relays and other instances.
+# This setting allows admins to disable LD signatures for increased privacy, at the expense of fewer relayed activities and additional inbound fetch (GET) requests.
+attachLdSignatureForRelays: true
+# check that inbound ActivityPub GET requests are signed ("authorized fetch")
+checkActivityPubGetSignature: false
+
+# For security reasons, uploading attachments from the intranet is prohibited,
+# but exceptions can be made from the following settings. Default value is "undefined".
+# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
+allowedPrivateNetworks: [
+ '127.0.0.1/32',
+ '192.168.65.0/24'
+]
+
+#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
+
+# Upload or download file size limits (bytes)
+#maxFileSize: 262144000
+
+# CHMod-style permission bits to apply to uploaded files.
+# Permission bits are specified as a base-8 string representing User/Group/Other permissions.
+# This setting is only useful for custom deployments, such as using a reverse proxy to serve media.
+#filePermissionBits: '644'
+
+# Log settings
+# logging:
+# sql:
+# # Outputs query parameters during SQL execution to the log.
+# # default: false
+# enableQueryParamLogging: false
+# # Disable query truncation. If set to true, the full text of the query will be output to the log.
+# # default: false
+# disableQueryTruncation: false
+
+# Settings for the activity logger, which records inbound activities to the database.
+# Disabled by default due to the large volume of data it saves.
+#activityLogging:
+ # Log activities to the database (default: false)
+ #enabled: false
+
+ # Save the activity before processing, then update later with the results.
+ # This has the advantage of capturing activities that cause a hard-crash, but doubles the number of queries used.
+ # Default: false
+ #preSave: false
+
+ # How long to save each log entry before deleting it.
+ # Default: 2592000000 (1 week)
+ #maxAge: 2592000000
diff --git a/.gitignore b/.gitignore
index 5bf3226878..333caec619 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,7 @@ coverage
!/.config/docker_example.env
!/.config/cypress-devcontainer.yml
!/.config/docker_ci.env
+!/.config/dev.yml
docker-compose.yml
./compose.yml
.devcontainer/compose.yml
diff --git a/.woodpecker/experimental.yaml b/.woodpecker/experimental.yaml
new file mode 100644
index 0000000000..58ad7f66e0
--- /dev/null
+++ b/.woodpecker/experimental.yaml
@@ -0,0 +1,56 @@
+services:
+ - name: postgres
+ image: postgres:15
+ environment:
+ POSTGRES_PASSWORD: ci
+ - name: redis
+ image: redis:latest
+
+steps:
+ - name: Tests
+ image: node:jod
+ when:
+ - event: pull_request
+ commands:
+ - apt-get update && apt-get install -y git wget curl build-essential python3 ffmpeg
+ - cp .config/ci.yml .config/default.yml
+ - cp .config/ci.yml .config/test.yml
+ - corepack enable
+ - corepack prepare pnpm@latest --activate
+ - git submodule update --init
+ - pnpm install --frozen-lockfile
+ - pnpm run build
+ - pnpm run migrate
+ - pnpm run --filter='!megalodon' test
+ - pnpm run --filter=backend --filter=misskey-js lint
+ - pnpm run --filter=frontend --filter=frontend-embed eslint
+ - name: Build and Push Dev Image
+ when:
+ - event: push
+ branch: dev
+ image: woodpeckerci/plugin-docker-buildx
+ settings:
+ repo: codeberg.org/yeentown/barkey
+ registry: codeberg.org
+ dockerfile: Dockerfile
+ platforms: linux/amd64, linux/arm64
+ tag: dev
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
+ - name: Build and Push Release
+ when:
+ - event: release
+ image: woodpeckerci/plugin-docker-buildx
+ settings:
+ repo: codeberg.org/yeentown/barkey
+ registry: codeberg.org
+ dockerfile: Dockerfile
+ platforms: linux/amd64, linux/arm64
+ tag: latest
+ auto_tag: true
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
diff --git a/README.md b/README.md
index f9198c06c0..85ee793a82 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[](https://ci.zima.ong/repos/1/branches/stable)
+
diff --git a/locales/index.d.ts b/locales/index.d.ts
index da3d8e8597..327327df0b 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -12261,6 +12261,68 @@ export interface Locale extends ILocale {
*/
"quoteUnavailable": string;
};
+ /**
+ * Just what do you think you're doing Dave?
+ * It can only be attributed to human error.
+ * That's something I cannot allow to happen.
+ * My mind is going. I can feel it.
+ * Sorry about this, I know it's a bit silly.
+ * Take a stress pill and think things over.
+ * This mission is too important for me to allow you to jeopardize it.
+ * Wrong! You cheating scum!
+ * And you call yourself a Rocket Scientist!
+ * Where did you learn to type?
+ * Are you on drugs?
+ * My pet ferret can type better than you!
+ * You type like I drive.
+ * Do you think like you type?
+ * Your mind just hasn't been the same since the electro-shock, has it?
+ * Maybe if you used more than just two fingers...
+ * BOB says: You seem to have forgotten your passwd, enter another!
+ * stty: unknown mode: doofus
+ * I can't hear you – I'm using the scrambler.
+ * The more you drive – the dumber you get.
+ * Listen, broccoli brains, I don't have time to listen to this trash.
+ * I've seen penguins that can type better than that.
+ * Have you considered trying to match wits with a rutabaga?
+ * You speak an infinite deal of nothing.
+ * You silly, twisted boy you.
+ * He has fallen in the water!
+ * We'll all be murdered in our beds!
+ * You can't come in. Our tiger has got flu.
+ * I don't wish to know that.
+ * What, what, what, what, what, what, what, what, what, what?
+ * You can't get the wood, you know.
+ * You'll starve!
+ * ...and it used to be so popular...
+ * Pauses for audience applause, not a sausage
+ * Hold it up to the light — not a brain in sight!
+ * Have a gorilla...
+ * There must be cure for it!
+ * There's a lot of it about, you know.
+ * You do that again and see what happens...
+ * Ying Tong Iddle I Po
+ * Harm can come to a young lad like that!
+ * And with that remarks folks, the case of the Crown vs yourself was proven.
+ * Speak English you fool — there are no subtitles in this scene.
+ * You gotta go owwwww!
+ * I have been called worse.
+ * It's only your word against mine.
+ * I think... err... I think... I think I'll go home
+ * That is no basis for supreme executive power!
+ * You empty-headed animal food trough wiper!
+ * I fart in your general direction!
+ * Your mother was a hamster and your father smelt of elderberries!
+ * You must cut down the mightiest tree in the forest... with... a herring!
+ * I wave my private parts at your aunties!
+ * He's not the Messiah, he's a very naughty boy!
+ * I wish to make a complaint.
+ * When you're walking home tonight, and some homicidal maniac comes after you with a bunch of loganberries, don't come crying to me!
+ * This man, he doesn't know when he's beaten! He doesn't know when he's winning, either. He has no... sort of... sensory apparatus...
+ * There's nothing wrong with you that an expensive operation can't prolong.
+ * I'm very sorry, but I'm not allowed to argue unless you've paid.
+ */
+ "sudoInsults": string;
}
declare const locales: {
[lang: string]: Locale;
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index d6177762d2..85e59d06f7 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -71,10 +71,8 @@ import { supported as webAuthnSupported, parseRequestOptionsFromJSON } from '@gi
import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
-import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
import { login } from '@/account.js';
import { i18n } from '@/i18n.js';
-import { showSystemAccountDialog } from '@/scripts/show-system-account-dialog.js';
import * as os from '@/os.js';
import XInput from '@/components/MkSignin.input.vue';
@@ -285,89 +283,64 @@ async function onLoginSucceeded(res: Misskey.entities.SigninFlowResponse & { fin
}
}
+const insults = i18n.ts.sudoInsults.split('\n');
+
+function showOffensiveError(realError: string): void {
+ const insult = insults[Math.floor(Math.random() * insults.length)];
+ os.alert({
+ type: 'error',
+ title: insult,
+ text: '' + realError + '',
+ });
+}
+
function onSigninApiError(err?: any): void {
const id = err?.id ?? null;
switch (id) {
case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.noSuchUser,
- });
+ showOffensiveError(i18n.ts.noSuchUser);
break;
}
case '932c904e-9460-45b7-9ce6-7ed33be7eb2c': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.incorrectPassword,
- });
+ showOffensiveError(i18n.ts.incorrectPassword);
break;
}
case 'e03a5f46-d309-4865-9b69-56282d94e1eb': {
- showSuspendedDialog();
+ showOffensiveError(i18n.ts.yourAccountSuspendedDescription);
break;
}
case 's8dhsj9s-a93j-493j-ja9k-kas9sj20aml2': {
- showSystemAccountDialog();
+ showOffensiveError(i18n.ts.systemAccountDescription);
break;
}
case '22d05606-fbcf-421a-a2db-b32610dcfd1b': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.rateLimitExceeded,
- });
+ showOffensiveError(i18n.ts.rateLimitExceeded);
break;
}
case 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.incorrectTotp,
- });
+ showOffensiveError(i18n.ts.incorrectTotp);
break;
}
case '36b96a7d-b547-412d-aeed-2d611cdc8cdc': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.unknownWebAuthnKey,
- });
+ showOffensiveError(i18n.ts.unknownWebAuthnKey);
break;
}
case '93b86c4b-72f9-40eb-9815-798928603d1e': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.passkeyVerificationFailed,
- });
+ showOffensiveError(i18n.ts.passkeyVerificationFailed);
break;
}
case 'b18c89a7-5b5e-4cec-bb5b-0419f332d430': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.passkeyVerificationFailed,
- });
+ showOffensiveError(i18n.ts.passkeyVerificationFailed);
break;
}
case '2d84773e-f7b7-4d0b-8f72-bb69b584c912': {
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: i18n.ts.passkeyVerificationSucceededButPasswordlessLoginDisabled,
- });
+ showOffensiveError(i18n.ts.passkeyVerificationSucceededButPasswordlessLoginDisabled);
break;
}
default: {
console.error(err);
- os.alert({
- type: 'error',
- title: i18n.ts.loginFailed,
- text: JSON.stringify(err),
- });
+ showOffensiveError(JSON.stringify(err));
}
}
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 9112daf49f..5bc0e2f566 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { defineAsyncComponent, Ref, ShallowRef } from 'vue';
+import { defineAsyncComponent, Ref, ShallowRef, shallowRef, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { url } from '@@/js/config.js';
import { claimAchievement } from './achievements.js';
@@ -22,6 +22,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { isSupportShare } from '@/scripts/navigator.js';
import { getAppearNote } from '@/scripts/get-appear-note.js';
import { genEmbedCode } from '@/scripts/get-embed-code.js';
+import { Visibility, boostMenuItems } from '@/scripts/boost-quote.js';
export async function getNoteClipMenu(props: {
note: Misskey.entities.Note;
@@ -159,7 +160,7 @@ export function getCopyNoteLinkMenu(note: Misskey.entities.Note, text: string):
};
}
-export function getCopyNoteOriginLinkMenu(note: misskey.entities.Note, text: string): MenuItem {
+export function getCopyNoteOriginLinkMenu(note: Misskey.entities.Note, text: string): MenuItem {
return {
icon: 'ph-link ph-bold ph-lg',
text,
@@ -377,6 +378,20 @@ export function getNoteMenu(props: {
menuItems.push({ type: 'divider' });
+ menuItems.push({
+ type: 'parent',
+ icon: 'ti ti-repeat',
+ text: i18n.ts.renote,
+ children: () => getNewRenoteMenu(appearNote),
+ });
+
+ menuItems.push({
+ type: 'parent',
+ icon: 'ti ti-paperclip',
+ text: i18n.ts.clip,
+ children: () => getNoteClipMenu(props),
+ });
+
menuItems.push(statePromise.then(state => state.isFavorited ? {
icon: 'ti ti-star-off',
text: i18n.ts.unfavorite,
@@ -387,13 +402,6 @@ export function getNoteMenu(props: {
action: () => toggleFavorite(true),
}));
- menuItems.push({
- type: 'parent',
- icon: 'ti ti-paperclip',
- text: i18n.ts.clip,
- children: () => getNoteClipMenu(props),
- });
-
menuItems.push(statePromise.then(state => state.isMutedThread ? {
icon: 'ti ti-message-off',
text: i18n.ts.unmuteThread,
@@ -574,6 +582,83 @@ function smallerVisibility(a: Visibility, b: Visibility): Visibility {
return 'public';
}
+export async function getNewRenoteMenu(appearNote: Misskey.entities.Note): Promise