Compare commits

..

No commits in common. "a6be33341ca096a99737ef2a252b1b08f7a105f8" and "ea7d4d323eafb809715b6e629cd072354a5b4fc1" have entirely different histories.

74 changed files with 305 additions and 1464 deletions

View File

@ -50,7 +50,7 @@ Configuration files are located in [`/.circleci`](/.circleci).
* Your PR should include all source files (e.g. `.png`, `.blend`) of your models (for later editing). * Your PR should include all source files (e.g. `.png`, `.blend`) of your models (for later editing).
* Your PR must include the glTF binary files (`.glb`) of your models. * Your PR must include the glTF binary files (`.glb`) of your models.
* Add a locale key `room.furnitures.YOUR_ITEM` at [`/locales/ja-JP.yml`](/locales/ja-JP.yml). * Add a locale key `room.furnitures.YOUR_ITEM` at [`/locales/ja-JP.yml`](/locales/ja-JP.yml).
* Add a furniture definition at [`src/client/scripts/room/furnitures.json5`](src/client/scripts/room/furnitures.json5). * Add a furniture definition at [`/src/client/app/common/scripts/room/furnitures.json5`](/src/client/app/common/scripts/room/furnitures.json5).
If you have no experience on 3D modeling, we suggest to use the free 3DCG software [Blender](https://www.blender.org/). If you have no experience on 3D modeling, we suggest to use the free 3DCG software [Blender](https://www.blender.org/).
You can find information on glTF 2.0 at [glTF 2.0 — Blender Manual]( https://docs.blender.org/manual/en/dev/addons/io_scene_gltf2.html). You can find information on glTF 2.0 at [glTF 2.0 — Blender Manual]( https://docs.blender.org/manual/en/dev/addons/io_scene_gltf2.html).

View File

@ -430,8 +430,6 @@ inUse: "مستخدم"
info: "عن" info: "عن"
user: "المستخدمون" user: "المستخدمون"
administration: "إدارة " administration: "إدارة "
expiration: "ينتهي استطلاع الرأي في"
middle: "متوسط"
_email: _email:
_follow: _follow:
title: "يتابعك" title: "يتابعك"

View File

@ -7,7 +7,6 @@ search: "Suchen"
notifications: "Benachrichtigungen" notifications: "Benachrichtigungen"
username: "Benutzername" username: "Benutzername"
password: "Passwort" password: "Passwort"
forgotPassword: "Passwort vergessen"
fetchingAsApObject: "Wird aus dem Fediverse angefragt..." fetchingAsApObject: "Wird aus dem Fediverse angefragt..."
ok: "OK" ok: "OK"
gotIt: "Verstanden!" gotIt: "Verstanden!"
@ -299,8 +298,8 @@ reject: "Ablehnen"
normal: "Normal" normal: "Normal"
instanceName: "Name der Instanz" instanceName: "Name der Instanz"
instanceDescription: "Beschreibung der Instanz" instanceDescription: "Beschreibung der Instanz"
maintainerName: "Administrator" maintainerName: "Betreiber"
maintainerEmail: "Administrator-Email" maintainerEmail: "Betreiber-Email"
tosUrl: "URL der Nutzungsbedingungen" tosUrl: "URL der Nutzungsbedingungen"
thisYear: "Dieses Jahr" thisYear: "Dieses Jahr"
thisMonth: "Dieser Monat" thisMonth: "Dieser Monat"
@ -738,7 +737,7 @@ user: "Benutzer"
administration: "Verwaltung" administration: "Verwaltung"
accounts: "Benutzerkonten" accounts: "Benutzerkonten"
switch: "Wechseln" switch: "Wechseln"
noMaintainerInformationWarning: "Administratorinformationen sind nicht konfiguriert." noMaintainerInformationWarning: "Betreiberinformationen sind nicht konfiguriert."
noBotProtectionWarning: "Bot-Schutz ist nicht konfiguriert." noBotProtectionWarning: "Bot-Schutz ist nicht konfiguriert."
configure: "Konfigurieren" configure: "Konfigurieren"
postToGallery: "Beitrag zu Galerie hinzufügen" postToGallery: "Beitrag zu Galerie hinzufügen"
@ -746,18 +745,6 @@ gallery: "Galerie"
recentPosts: "Neue Beiträge" recentPosts: "Neue Beiträge"
popularPosts: "Beliebte Beiträge" popularPosts: "Beliebte Beiträge"
shareWithNote: "Mit Notiz teilen" shareWithNote: "Mit Notiz teilen"
ads: "Werbung"
expiration: "Frist"
memo: "Merkzettel"
priority: "Priorität"
high: "Hoch"
middle: "Mittel"
low: "Niedrig"
emailNotConfiguredWarning: "Keine Email-Adresse hinterlegt"
_forgotPassword:
enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit der du dein Passwort zurücksetzen kannst."
ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator."
contactAdmin: "Diese Instanz unterstützt die Verwendung von Email-Adressen nicht. Wende dich, um dein Passwort zurückzusetzen, an den Administrator."
_gallery: _gallery:
my: "Meine Galerie" my: "Meine Galerie"
liked: "Beiträge, die mir gefallen" liked: "Beiträge, die mir gefallen"

View File

@ -7,7 +7,6 @@ search: "Search"
notifications: "Notifications" notifications: "Notifications"
username: "Username" username: "Username"
password: "Password" password: "Password"
forgotPassword: "Forgot password"
fetchingAsApObject: "Fetching from Fediverse..." fetchingAsApObject: "Fetching from Fediverse..."
ok: "OK" ok: "OK"
gotIt: "Got it!" gotIt: "Got it!"
@ -731,7 +730,6 @@ active: "Active"
offline: "Offline" offline: "Offline"
notRecommended: "Not recommended" notRecommended: "Not recommended"
botProtection: "Bot Protection" botProtection: "Bot Protection"
instanceBlocking: "Blocked Instances"
selectAccount: "Select account" selectAccount: "Select account"
enabled: "Enabled" enabled: "Enabled"
disabled: "Disabled" disabled: "Disabled"
@ -748,18 +746,6 @@ gallery: "Gallery"
recentPosts: "Recent posts" recentPosts: "Recent posts"
popularPosts: "Popular posts" popularPosts: "Popular posts"
shareWithNote: "Share with note" shareWithNote: "Share with note"
ads: "Advertisements"
expiration: "Deadline"
memo: "Memo"
priority: "Priority"
high: "High"
middle: "Medium"
low: "Low"
emailNotConfiguredWarning: "Email address not set"
_forgotPassword:
enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it."
ifNoEmail: "If you did not use an email during registration, please contact the administrator instead."
contactAdmin: "This instance does not support using email addresses, please contact the administrator to reset your password instead."
_gallery: _gallery:
my: "My Gallery" my: "My Gallery"
liked: "Liked Posts" liked: "Liked Posts"
@ -1082,7 +1068,7 @@ _auth:
permissionAsk: "This application requires following permissions:" permissionAsk: "This application requires following permissions:"
pleaseGoBack: "Please go back to the application" pleaseGoBack: "Please go back to the application"
callback: "Returning back to the application" callback: "Returning back to the application"
denied: "Access denied" denied: "Access Denied"
_antennaSources: _antennaSources:
all: "All notes" all: "All notes"
homeTimeline: "Notes from following users" homeTimeline: "Notes from following users"

View File

@ -1,6 +1,5 @@
--- ---
_lang_: "Español" _lang_: "Español"
headlineMisskey: "Red conectada por notas"
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\nExplora un nuevo mundo 🚀" introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\nExplora un nuevo mundo 🚀"
monthAndDay: "{day}/{month}" monthAndDay: "{day}/{month}"
search: "Buscar" search: "Buscar"
@ -138,7 +137,6 @@ flagAsBotDescription: "En caso de que esta cuenta fuera usada por un programa, a
flagAsCat: "Esta cuenta es un gato" flagAsCat: "Esta cuenta es un gato"
flagAsCatDescription: "En caso de que declare que esta cuenta es de un gato, active esta opción." flagAsCatDescription: "En caso de que declare que esta cuenta es de un gato, active esta opción."
autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues" autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues"
addAccount: "Agregar Cuenta"
loginFailed: "Error al iniciar sesión." loginFailed: "Error al iniciar sesión."
showOnRemote: "Ver en una instancia remota" showOnRemote: "Ver en una instancia remota"
general: "General" general: "General"
@ -437,7 +435,6 @@ signinWith: "Inicie sesión con {x}"
signinFailed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario y contraseña correctos." signinFailed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario y contraseña correctos."
tapSecurityKey: "Toque la clave de seguridad" tapSecurityKey: "Toque la clave de seguridad"
or: "O" or: "O"
language: "Idioma"
uiLanguage: "Idioma de visualización de la interfaz" uiLanguage: "Idioma de visualización de la interfaz"
groupInvited: "Invitado al grupo" groupInvited: "Invitado al grupo"
aboutX: "Acerca de {x}" aboutX: "Acerca de {x}"
@ -452,7 +449,6 @@ category: "Categoría"
tags: "Etiqueta" tags: "Etiqueta"
docSource: "Fuente de este documento" docSource: "Fuente de este documento"
createAccount: "Crear cuenta" createAccount: "Crear cuenta"
existingAccount: "Cuenta existente"
regenerate: "Regenerar" regenerate: "Regenerar"
fontSize: "Tamaño de la letra" fontSize: "Tamaño de la letra"
noFollowRequests: "No hay solicitudes de seguimiento" noFollowRequests: "No hay solicitudes de seguimiento"
@ -567,7 +563,6 @@ pluginTokenRequestedDescription: "Este plugin podrá usar los permisos descritos
notificationType: "Tipo de notificación" notificationType: "Tipo de notificación"
edit: "Editar" edit: "Editar"
useStarForReactionFallback: "En caso de que los emojis de reacciones no sean claros, usar en su lugar una estrella" useStarForReactionFallback: "En caso de que los emojis de reacciones no sean claros, usar en su lugar una estrella"
emailServer: "Servidor de correo"
enableEmail: "Activar el envío de correos electrónicos" enableEmail: "Activar el envío de correos electrónicos"
emailConfigInfo: "Usar en caso de validación de correo electrónico y pedido de contraseña" emailConfigInfo: "Usar en caso de validación de correo electrónico y pedido de contraseña"
email: "Correo" email: "Correo"
@ -647,14 +642,6 @@ driveFilesCount: "Cantidad de archivos en el drive"
driveUsage: "Uso del drive" driveUsage: "Uso del drive"
noCrawle: "Rechazar indexación del crawler" noCrawle: "Rechazar indexación del crawler"
noCrawleDescription: "Pedir a los motores de búsqueda que no indexen tu perfil, notas, páginas, etc." noCrawleDescription: "Pedir a los motores de búsqueda que no indexen tu perfil, notas, páginas, etc."
alwaysMarkSensitive: "Marcar los medios de comunicación como contenido sensible por defecto"
verificationEmailSent: "Se le ha enviado un correo electrónico de confirmación. Por favor, acceda al enlace proporcionado en el correo electrónico para completar la configuración."
notSet: "Sin especificar"
emailVerified: "Su dirección de correo electrónico ha sido verificada."
noteFavoritesCount: "Número de notas favoritas"
pageLikesCount: "Número de favoritos en la página"
pageLikedCount: "Número de favoritos de su página"
contact: "Contacto"
clips: "Clip" clips: "Clip"
clearCache: "Limpiar caché" clearCache: "Limpiar caché"
backgroundColor: "Fondo" backgroundColor: "Fondo"
@ -665,8 +652,6 @@ goBack: "Deseleccionar"
info: "Información" info: "Información"
user: "Usuarios" user: "Usuarios"
administration: "Administrar" administration: "Administrar"
expiration: "Termina el"
middle: "Mediano"
_gallery: _gallery:
unlike: "Quitar me gusta" unlike: "Quitar me gusta"
_email: _email:

View File

@ -7,7 +7,6 @@ search: "Rechercher"
notifications: "Notifications" notifications: "Notifications"
username: "Nom dutilisateur·rice" username: "Nom dutilisateur·rice"
password: "Mot de passe" password: "Mot de passe"
forgotPassword: "Mot de passe oublié"
fetchingAsApObject: "Récupération depuis le fédiverse …" fetchingAsApObject: "Récupération depuis le fédiverse …"
ok: "OK" ok: "OK"
gotIt: "Jai compris !" gotIt: "Jai compris !"
@ -605,7 +604,6 @@ setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les sé
fileIdOrUrl: "ID du fichier ou URL" fileIdOrUrl: "ID du fichier ou URL"
chatOpenBehavior: "Comportement de la fenêtre de discussion lors de son ouverture" chatOpenBehavior: "Comportement de la fenêtre de discussion lors de son ouverture"
behavior: "Comportement" behavior: "Comportement"
sample: "Exemple"
abuseReports: "Signalements" abuseReports: "Signalements"
reportAbuse: "Signalements" reportAbuse: "Signalements"
reportAbuseOf: "Signaler {name}" reportAbuseOf: "Signaler {name}"
@ -727,7 +725,6 @@ onlineStatus: "Statut"
hideOnlineStatus: "Se rendre invisible" hideOnlineStatus: "Se rendre invisible"
hideOnlineStatusDescription: "Rendre votre statut invisible peut diminuer les performances de certaines fonctionnalités, telles que la Recherche." hideOnlineStatusDescription: "Rendre votre statut invisible peut diminuer les performances de certaines fonctionnalités, telles que la Recherche."
online: "En ligne" online: "En ligne"
active: "Actif·ve"
offline: "Hors ligne" offline: "Hors ligne"
notRecommended: "Déconseillé" notRecommended: "Déconseillé"
botProtection: "Protection contre les bots" botProtection: "Protection contre les bots"
@ -748,18 +745,6 @@ gallery: "Galerie"
recentPosts: "Les plus récentes" recentPosts: "Les plus récentes"
popularPosts: "Les plus consultées" popularPosts: "Les plus consultées"
shareWithNote: "Partager dans une note" shareWithNote: "Partager dans une note"
ads: "Publicité"
expiration: "Échéance"
memo: "Mémo"
priority: "Priorité"
high: "Haute"
middle: "Moyen"
low: "Basse"
emailNotConfiguredWarning: "Vous n'avez pas configuré d'adresse e-mail."
_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 adresse."
ifNoEmail: "Si vous n'avez pas enregistré d'adresse e-mail, merci de contacter l'administrateur·rice de votre instance."
contactAdmin: "Cette instance ne permettant pas l'utilisation d'adresses e-mail, prenez contact avec l'administrateur·rice pour procéder à la réinitialisation de votre mot de passe."
_gallery: _gallery:
my: "Mes publications" my: "Mes publications"
liked: " Publications que j'ai aimées" liked: " Publications que j'ai aimées"
@ -775,7 +760,6 @@ _plugin:
installWarn: "Ninstallez que des extensions provenant de sources de confiance." installWarn: "Ninstallez que des extensions provenant de sources de confiance."
manage: "Gestion des plugins" manage: "Gestion des plugins"
_registry: _registry:
scope: "Portée"
key: "Clé " key: "Clé "
keys: "Clé " keys: "Clé "
domain: "Domaine" domain: "Domaine"
@ -809,38 +793,18 @@ _mfm:
boldDescription: "Il est possible de mettre le texte en exergue en le mettant en gras." boldDescription: "Il est possible de mettre le texte en exergue en le mettant en gras."
small: "Diminuer l'emphase" small: "Diminuer l'emphase"
smallDescription: "Le contenu peut être affiché en petit et fin." smallDescription: "Le contenu peut être affiché en petit et fin."
center: "Centrer" center: "Centrée"
centerDescription: "Le contenu peut être centré" centerDescription: "Le contenu peut être centré"
inlineCode: "Code (inline)" inlineCode: "Code (inline)"
inlineCodeDescription: "Coloration syntaxique des lignes de code."
blockCode: "Bloc de code" blockCode: "Bloc de code"
blockCodeDescription: "Coloration syntaxique des lignes de code pour les blocs multi-lignes."
inlineMath: "Formule mathématique (inline)" inlineMath: "Formule mathématique (inline)"
inlineMathDescription: "Afficher les formules mathématiques (KaTeX)."
blockMath: "Formule mathématique (bloc)" blockMath: "Formule mathématique (bloc)"
blockMathDescription: "Afficher les formules mathématiques (KaTeX) multi-lignes dans un bloc."
quote: "Citer" quote: "Citer"
quoteDescription: "Affiche le contenu sous forme de citation." quoteDescription: "Affiche le contenu sous forme de citation."
emoji: "Émojis personnalisés" emoji: "Émojis personnalisés"
emojiDescription: "Entourez le nom de l'émoji personnalisé de deux points pour l'afficher."
search: "Rechercher" search: "Rechercher"
searchDescription: "Affiche une boîte de recherche avec du texte pré-saisi."
flip: "Inverser" flip: "Inverser"
flipDescription: "Rotation verticale ou horizontale du contenu" flipDescription: "Rotation verticale ou horizontale du contenu"
jelly: "Animation (Gelée)"
jellyDescription: "Donne une animation d'étirement."
tada: "Animation (Tada)"
tadaDescription: "Donne une animation qui donne une impression de \"Tada !\""
jump: "Animation (Saut)"
jumpDescription: "Donne une animation qui saute."
bounce: "Animation (Rebond)"
bounceDescription: "Donne une animation de rebondissement."
shake: "Animation (Secousse)"
shakeDescription: "Donne une animation tremblante."
twitch: "Animation (Tremblement)"
twitchDescription: "Donne une animation de tremblement intense."
spin: "Animation (Rotation)"
spinDescription: "Donne une animation de rotation."
x2: "Grand" x2: "Grand"
x2Description: "Afficher le contenu en grand." x2Description: "Afficher le contenu en grand."
x3: "Très grand" x3: "Très grand"
@ -882,7 +846,6 @@ _reversi:
ended: "Fin de partie" ended: "Fin de partie"
playing: "En cours" playing: "En cours"
isLlotheo: "Celui ou celle qui a le moins de pièces gagne (Llotheo)" isLlotheo: "Celui ou celle qui a le moins de pièces gagne (Llotheo)"
loopedMap: "Carte en boucle"
canPutEverywhere: "Les pions peuvent être placés partout " canPutEverywhere: "Les pions peuvent être placés partout "
_instanceTicker: _instanceTicker:
none: "Cacher " none: "Cacher "
@ -932,13 +895,11 @@ _theme:
constant: "Constante" constant: "Constante"
defaultValue: "Valeur par défaut" defaultValue: "Valeur par défaut"
color: "Couleur" color: "Couleur"
refProp: "Appeler une propriété" refConst: "Référencez une constante"
refConst: "Appeler une constante"
key: "Clé " key: "Clé "
func: "Fonction" func: "Fonction"
funcKind: "Type de fonction" funcKind: "Type de fonction"
argument: "Argument" argument: "Argument"
basedProp: "Nom de la propriété référencée"
alpha: "Transparence" alpha: "Transparence"
darken: "Sombre" darken: "Sombre"
lighten: "Clair" lighten: "Clair"
@ -964,12 +925,9 @@ _theme:
mention: "Mentionner" mention: "Mentionner"
mentionMe: "Mentions (Moi)" mentionMe: "Mentions (Moi)"
renote: "Partager" renote: "Partager"
modalBg: "Modal d'arrière-plan"
divider: "Séparateur" divider: "Séparateur"
scrollbarHandle: "Poignée de la barre de navigation" scrollbarHandle: "Poignée de la barre de navigation"
scrollbarHandleHover: "Poignée de la barre de navigation (survolée)" scrollbarHandleHover: "Poignée de la barre de navigation (survolée)"
dateLabelFg: "Texte de l'étiquette de la date"
infoBg: "Arrière-plan pour les informations"
infoFg: "Texte d'information" infoFg: "Texte d'information"
infoWarnBg: "Arrière-plan des avertissements" infoWarnBg: "Arrière-plan des avertissements"
infoWarnFg: "Texte davertissement" infoWarnFg: "Texte davertissement"
@ -983,7 +941,6 @@ _theme:
inputBorder: "Cadre de la zone de texte" inputBorder: "Cadre de la zone de texte"
listItemHoverBg: "Arrière-plan d'item de liste (survolé)" listItemHoverBg: "Arrière-plan d'item de liste (survolé)"
driveFolderBg: "Arrière-plan du dossier de disque" driveFolderBg: "Arrière-plan du dossier de disque"
wallpaperOverlay: "Superposition de fond d'écran"
badge: "Badge" badge: "Badge"
messageBg: "Arrière plan de la discussion" messageBg: "Arrière plan de la discussion"
accentDarken: "Plus sombre" accentDarken: "Plus sombre"
@ -994,7 +951,7 @@ _sfx:
noteMy: "Ma note" noteMy: "Ma note"
notification: "Notifications" notification: "Notifications"
chat: "Discuter" chat: "Discuter"
chatBg: "Discussion (arrière-plan)" chatBg: "Discuter (De fond)"
antenna: "Réception de lantenne" antenna: "Réception de lantenne"
channel: "Notifications de canal" channel: "Notifications de canal"
reversiPutBlack: "Reversi : les pions noirs ont joué" reversiPutBlack: "Reversi : les pions noirs ont joué"
@ -1039,14 +996,14 @@ _tutorial:
step7_2: "Si vous désirez en savoir plus sur Misskey, jetez un œil sur la section {help}." step7_2: "Si vous désirez en savoir plus sur Misskey, jetez un œil sur la section {help}."
step7_3: "Bon courage et amusez-vous bien sur Misskey ! 🚀" step7_3: "Bon courage et amusez-vous bien sur Misskey ! 🚀"
_2fa: _2fa:
alreadyRegistered: "Configuration déjà achevée." alreadyRegistered: "Cette étape à déjà été complétée"
registerDevice: "Ajouter un nouvel appareil" registerDevice: "Ajouter un nouvel appareil"
registerKey: "Enregistrer une clef" registerKey: "Sinscrire la clé"
step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil." step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil."
step2: "Ensuite, scannez le code QR affiché sur lécran." step2: "Ensuite, scannez le code QR affiché sur lécran."
step3: "Entrez le jeton affiché sur votre application pour compléter la configuration." step3: "Entrez le jeton affiché sur votre application pour compléter la configuration."
step4: "À partir de maintenant, ce même jeton vous sera demandé à chacune de vos connexions." step4: "Lorsque vous vous connectez, entrez le jeton de la même manière."
securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion grâce à une clé de sécurité matérielle qui prend en charge FIDO2, ou bien en configurant l'authentification par empreinte digitale ou par code PIN sur votre appareil." securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion avec non seulement la clé de sécurité matérielle qui prend en charge FIDO2, mais également l'authentification par empreinte digitale ou PIN sur votre appareil."
_permissions: _permissions:
"read:account": "Afficher les informations du compte" "read:account": "Afficher les informations du compte"
"write:account": "Mettre à jour les informations de votre compte" "write:account": "Mettre à jour les informations de votre compte"
@ -1058,8 +1015,8 @@ _permissions:
"write:favorites": "Gérer les favoris" "write:favorites": "Gérer les favoris"
"read:following": "Voir les informations de vos abonnements" "read:following": "Voir les informations de vos abonnements"
"write:following": "Abonnements/Se désabonner" "write:following": "Abonnements/Se désabonner"
"read:messaging": "Voir vos discussions" "read:messaging": "Cherche à discuter"
"write:messaging": "Gérer les discussions" "write:messaging": "Contrôler le discuter"
"read:mutes": "Voir les comptes masqués" "read:mutes": "Voir les comptes masqués"
"write:mutes": "Gérer les comptes masqués" "write:mutes": "Gérer les comptes masqués"
"write:notes": "Créer / supprimer des notes" "write:notes": "Créer / supprimer des notes"
@ -1068,10 +1025,10 @@ _permissions:
"read:reactions": "Lire les réactions" "read:reactions": "Lire les réactions"
"write:reactions": "Gérer vos réactions" "write:reactions": "Gérer vos réactions"
"write:votes": "Voter" "write:votes": "Voter"
"read:pages": "Voir vos pages" "read:pages": "Afficher la page"
"write:pages": "Gérer les pages" "write:pages": "Mettre à jour les Pages"
"read:page-likes": "Voir les mentions « J'aime » des pages" "read:page-likes": "Voir les favoris sur les Pages"
"write:page-likes": "Gérer les mentions « J'aime » sur les pages" "write:page-likes": "Mettre à jour les favoris sur les Pages"
"read:user-groups": "Voir les groupes d'utilisateur·rice·s" "read:user-groups": "Voir les groupes d'utilisateur·rice·s"
"write:user-groups": "Éditer les groupes des utilisateur·rice·s" "write:user-groups": "Éditer les groupes des utilisateur·rice·s"
"read:channels": "Lire les canaux" "read:channels": "Lire les canaux"

View File

@ -628,7 +628,6 @@ driveFilesCount: "Numero di file nel Drive"
driveUsage: "Utilizzazione del Drive" driveUsage: "Utilizzazione del Drive"
noCrawle: "Rifiuta l'indicizzazione dai robot." noCrawle: "Rifiuta l'indicizzazione dai robot."
noCrawleDescription: "Richiedi che i motori di ricerca non indicizzino la tua pagina di profilo, le tue note, pagine, ecc." noCrawleDescription: "Richiedi che i motori di ricerca non indicizzino la tua pagina di profilo, le tue note, pagine, ecc."
lockedAccountInfo: "A meno che non imposti la visibilità delle tue note su \"Solo ai follower\", le tue note sono visibili da tutti, anche se hai configurato l'account per confermare manualmente le richieste di follow."
alwaysMarkSensitive: "Segnare i media come sensibili per impostazione predefinita" alwaysMarkSensitive: "Segnare i media come sensibili per impostazione predefinita"
loadRawImages: "Visualizza le intere immagini allegate invece delle miniature." loadRawImages: "Visualizza le intere immagini allegate invece delle miniature."
disableShowingAnimatedImages: "Disabilita le immagini animate" disableShowingAnimatedImages: "Disabilita le immagini animate"
@ -707,7 +706,6 @@ online: "Online"
offline: "Offline" offline: "Offline"
notRecommended: "Sconsigliato" notRecommended: "Sconsigliato"
botProtection: "Protezione contro i bot" botProtection: "Protezione contro i bot"
instanceBlocking: "Istanze bloccate"
selectAccount: "Scegli account" selectAccount: "Scegli account"
enabled: "Attivo" enabled: "Attivo"
disabled: "Inattivo" disabled: "Inattivo"
@ -724,8 +722,6 @@ gallery: "Galleria"
recentPosts: "Le più recenti" recentPosts: "Le più recenti"
popularPosts: "Le più visualizzate" popularPosts: "Le più visualizzate"
shareWithNote: "Condividere in nota" shareWithNote: "Condividere in nota"
expiration: "Scadenza"
middle: "Predefinito"
_gallery: _gallery:
my: "Le mie pubblicazioni" my: "Le mie pubblicazioni"
liked: "Pubblicazioni che mi piacciono" liked: "Pubblicazioni che mi piacciono"
@ -833,15 +829,11 @@ _theme:
constant: "Costante" constant: "Costante"
defaultValue: "Valore predefinito" defaultValue: "Valore predefinito"
color: "Colore" color: "Colore"
refConst: "Chiama costante"
key: "Chiave" key: "Chiave"
func: "Funzione" func: "Funzione"
funcKind: "Tipo di funzione"
argument: "Argomento" argument: "Argomento"
darken: "Scuro" darken: "Scuro"
lighten: "Chiaro" lighten: "Chiaro"
inputConstantName: "Inserisci un nome per la costante"
deleteConstantConfirm: "Vuoi davvero eliminare la costante {const}?"
keys: keys:
bg: "Sfondo" bg: "Sfondo"
fg: "Testo" fg: "Testo"
@ -858,10 +850,8 @@ _theme:
link: "Link" link: "Link"
hashtag: "Hashtag" hashtag: "Hashtag"
mention: "Menzioni" mention: "Menzioni"
mentionMe: "Menzioni (di me)"
renote: "Rinota" renote: "Rinota"
divider: "Interruzione di linea" divider: "Interruzione di linea"
infoBg: "Sfondo informazioni"
infoFg: "Testo di informazioni" infoFg: "Testo di informazioni"
infoWarnBg: "Sfondo degli avvisi" infoWarnBg: "Sfondo degli avvisi"
infoWarnFg: "Testo di avviso" infoWarnFg: "Testo di avviso"
@ -875,13 +865,11 @@ _theme:
inputBorder: "Inquadra casella di testo" inputBorder: "Inquadra casella di testo"
listItemHoverBg: "Sfondo della voce di elenco (sorvolato)" listItemHoverBg: "Sfondo della voce di elenco (sorvolato)"
driveFolderBg: "Sfondo della cartella di disco" driveFolderBg: "Sfondo della cartella di disco"
messageBg: "Sfondo della chat"
_sfx: _sfx:
note: "Nota" note: "Nota"
noteMy: "Mia nota" noteMy: "Mia nota"
notification: "Notifiche" notification: "Notifiche"
chat: "Messaggi" chat: "Messaggi"
chatBg: "Chat (sfondo)"
antenna: "Ricezione dell'antenna" antenna: "Ricezione dell'antenna"
channel: "Notifiche di canale" channel: "Notifiche di canale"
_ago: _ago:
@ -926,18 +914,12 @@ _tutorial:
_2fa: _2fa:
registerDevice: "Aggiungi dispositivo" registerDevice: "Aggiungi dispositivo"
_permissions: _permissions:
"read:account": "Visualizzare le informazioni dell'account"
"write:account": "Modificare le informazioni dell'account"
"read:blocks": "Visualizza gli account bloccati" "read:blocks": "Visualizza gli account bloccati"
"write:blocks": "Gestisci gli account bloccati" "write:blocks": "Gestisci gli account bloccati"
"read:drive": "Aprire il Drive"
"write:drive": "Gestire il Drive"
"read:favorites": "Visualizza i tuoi preferiti" "read:favorites": "Visualizza i tuoi preferiti"
"write:favorites": "Gestisci i tuoi preferiti" "write:favorites": "Gestisci i tuoi preferiti"
"read:following": "Vedi le informazioni di follow" "read:following": "Vedi le informazioni di follow"
"write:following": "Seguiti/ Smetti di seguire" "write:following": "Seguiti/ Smetti di seguire"
"read:messaging": "Visualizzare la chat"
"write:messaging": "Gestire la chat"
"read:mutes": "Vedi account silenziati" "read:mutes": "Vedi account silenziati"
"write:mutes": "Gerisci account silenziati" "write:mutes": "Gerisci account silenziati"
"write:notes": "Creare / Eliminare note" "write:notes": "Creare / Eliminare note"
@ -945,22 +927,12 @@ _permissions:
"write:notifications": "Gerisci notifiche" "write:notifications": "Gerisci notifiche"
"read:reactions": "Vedi reazioni" "read:reactions": "Vedi reazioni"
"write:reactions": "Gerisci reazioni" "write:reactions": "Gerisci reazioni"
"write:votes": "Votare"
"read:pages": "Visualizzare pagine"
"write:pages": "Gestire pagine"
"read:page-likes": "Visualizzare i \"Mi piace\" di pagine"
"write:page-likes": "Gestire i \"Mi piace\" di pagine"
"read:user-groups": "Vedi gruppi di utenti" "read:user-groups": "Vedi gruppi di utenti"
"write:user-groups": "Gestisci gruppi di utenti" "write:user-groups": "Gestisci gruppi di utenti"
"read:channels": "Visualizza canali" "read:channels": "Visualizza canali"
"write:channels": "Gerisci canali" "write:channels": "Gerisci canali"
_auth: _auth:
shareAccess: "Autorizzare「{name}」ad accedere al tuo account?" shareAccess: "Autorizzare「{name}」ad accedere al tuo account?"
shareAccessAsk: "Vuoi davvero consentire l'accesso al tuo account a questa app'?"
permissionAsk: "Questa app richiede le seguenti autorizzazioni:"
pleaseGoBack: "Si prega di ritornare sulla app"
callback: "Ritornando sulla app"
denied: "Accesso negato"
_antennaSources: _antennaSources:
all: "Tutte le note" all: "Tutte le note"
homeTimeline: "Note dagli utenti che segui" homeTimeline: "Note dagli utenti che segui"
@ -988,39 +960,22 @@ _widgets:
digitalClock: "Orologio digitale" digitalClock: "Orologio digitale"
federation: "Federazione" federation: "Federazione"
postForm: "Finestra di pubblicazione" postForm: "Finestra di pubblicazione"
slideshow: "Diapositive"
button: "Pulsante" button: "Pulsante"
onlineUsers: "Utenti online" onlineUsers: "Utenti online"
jobQueue: "Coda di lavoro" jobQueue: "Coda di lavoro"
serverMetric: "Statistiche server" serverMetric: "Statistiche server"
aiscript: "Console AiScript"
_cw: _cw:
hide: "Nascondere" hide: "Nascondere"
show: "Mostra di più" show: "Mostra di più"
chars: "{count} caratteri"
files: "{count} file"
_poll: _poll:
noOnlyOneChoice: "Sono necessarie almeno 2 risposte"
choiceN: "Opzione {n}"
noMore: "Hai aggiunto il numero massimo di opzioni." noMore: "Hai aggiunto il numero massimo di opzioni."
canMultipleVote: "Possibilità di risposte multiple" canMultipleVote: "Risposte multiple"
expiration: "Scadenza" expiration: "Scadenza"
infinite: "Non scade" infinite: "Permanente"
at: "Seleziona data"
after: "Seleziona durata"
deadlineDate: "Data di scadenza" deadlineDate: "Data di scadenza"
deadlineTime: "Ora di scadenza" deadlineTime: "h"
duration: "Durata" voted: "Votato"
votesCount: "{n} voti"
totalVotes: "Totale di {n} voti"
vote: "Vota"
showResult: "Visualizza risultati"
voted: "Hai votato"
closed: "Terminato" closed: "Terminato"
remainingDays: "Rimangono {d} giorni e {h} ore"
remainingHours: "Rimangono {h} ore e {m} minuti"
remainingMinutes: "Rimangono {m} minuti e {s} secondi"
remainingSeconds: "Rimangono {s} secondi"
_visibility: _visibility:
public: "Pubblica" public: "Pubblica"
publicDescription: "Visibile per tutti sul Fediverso" publicDescription: "Visibile per tutti sul Fediverso"
@ -1048,7 +1003,7 @@ _profile:
username: "Nome utente" username: "Nome utente"
description: "Bio" description: "Bio"
youCanIncludeHashtags: "Puoi anche includere hashtag." youCanIncludeHashtags: "Puoi anche includere hashtag."
metadata: "Informazioni aggiuntive" metadata: "Metadati"
metadataEdit: "Modifica informazioni aggiuntive" metadataEdit: "Modifica informazioni aggiuntive"
metadataDescription: "Puoi pubblicare fino a quattro informazioni aggiuntive sul profilo." metadataDescription: "Puoi pubblicare fino a quattro informazioni aggiuntive sul profilo."
metadataLabel: "Etichetta" metadataLabel: "Etichetta"
@ -1357,7 +1312,7 @@ _notification:
youGotReply: "{name} ti ha risposto" youGotReply: "{name} ti ha risposto"
youGotQuote: "{name} ha citato il tuo Nota e ha detto" youGotQuote: "{name} ha citato il tuo Nota e ha detto"
youRenoted: "{name} ha rinotato" youRenoted: "{name} ha rinotato"
youGotPoll: "{name} ha votato" youGotPoll: "{name} ha volluto."
youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio"
youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat" youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat"
youWereFollowed: "Ha iniziato a seguirti" youWereFollowed: "Ha iniziato a seguirti"

View File

@ -7,7 +7,6 @@ search: "検索"
notifications: "通知" notifications: "通知"
username: "ユーザー名" username: "ユーザー名"
password: "パスワード" password: "パスワード"
forgotPassword: "パスワードを忘れた"
fetchingAsApObject: "連合に照会中" fetchingAsApObject: "連合に照会中"
ok: "OK" ok: "OK"
gotIt: "わかった" gotIt: "わかった"
@ -748,19 +747,6 @@ gallery: "ギャラリー"
recentPosts: "最近の投稿" recentPosts: "最近の投稿"
popularPosts: "人気の投稿" popularPosts: "人気の投稿"
shareWithNote: "ノートで共有" shareWithNote: "ノートで共有"
ads: "広告"
expiration: "期限"
memo: "メモ"
priority: "優先度"
high: "高"
middle: "中"
low: "低"
emailNotConfiguredWarning: "メールアドレスの設定がされていません。"
_forgotPassword:
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
ifNoEmail: "メールアドレスを登録していない場合は、管理者までお問い合わせください。"
contactAdmin: "このインスタンスではメールがサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。"
_gallery: _gallery:
my: "自分の投稿" my: "自分の投稿"

View File

@ -552,7 +552,7 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
testEmail: "配信テスト" testEmail: "配信テスト"
wordMute: "ワードミュート" wordMute: "ワードミュート"
userSaysSomething: "{name}が何か言ったようやで" userSaysSomething: "{name}が何か言ったようやで"
makeActive: "使うで" makeActive: "アクティブにしてや"
display: "表示" display: "表示"
copy: "コピー" copy: "コピー"
metrics: "メトリクス" metrics: "メトリクス"
@ -643,12 +643,6 @@ goBack: "戻る"
info: "情報" info: "情報"
user: "ユーザー" user: "ユーザー"
administration: "管理" administration: "管理"
ads: "広告"
expiration: "期限"
memo: "メモ"
high: "高い"
middle: "中"
low: "低い"
_gallery: _gallery:
unlike: "良くないわ" unlike: "良くないわ"
_email: _email:

View File

@ -747,8 +747,6 @@ gallery: "갤러리"
recentPosts: "최근 포스트" recentPosts: "최근 포스트"
popularPosts: "인기 포스트" popularPosts: "인기 포스트"
shareWithNote: "노트로 공유" shareWithNote: "노트로 공유"
expiration: "투표 기한"
middle: "보통"
_gallery: _gallery:
my: "내 갤러리" my: "내 갤러리"
liked: "좋아요 한 갤러리" liked: "좋아요 한 갤러리"

View File

@ -647,8 +647,6 @@ goBack: "Wróć"
info: "Informacje" info: "Informacje"
user: "Użytkownicy" user: "Użytkownicy"
administration: "Zarządzanie" administration: "Zarządzanie"
expiration: "Ankieta kończy się"
middle: "Średnie"
_gallery: _gallery:
unlike: "Cofnij polubienie" unlike: "Cofnij polubienie"
_email: _email:

View File

@ -747,8 +747,6 @@ gallery: "Галерея"
recentPosts: "Недавние публикации" recentPosts: "Недавние публикации"
popularPosts: "Популярные публикации" popularPosts: "Популярные публикации"
shareWithNote: "Поделиться заметкой" shareWithNote: "Поделиться заметкой"
expiration: "Опрос длится"
middle: "Средне"
_gallery: _gallery:
my: "Личная" my: "Личная"
liked: "Понравившееся" liked: "Понравившееся"

View File

@ -689,8 +689,6 @@ goBack: "Назад"
info: "Інформація" info: "Інформація"
user: "Користувачі" user: "Користувачі"
administration: "Управління" administration: "Управління"
expiration: "Опитування закінчується"
middle: "Середній"
_gallery: _gallery:
unlike: "Не вподобати" unlike: "Не вподобати"
_email: _email:

View File

@ -747,13 +747,11 @@ gallery: "图库"
recentPosts: "最新发布" recentPosts: "最新发布"
popularPosts: "热门投稿" popularPosts: "热门投稿"
shareWithNote: "在帖子中分享" shareWithNote: "在帖子中分享"
expiration: "截止时间"
middle: "中"
_gallery: _gallery:
my: "我的图库" my: "我的图库"
liked: "喜欢的图片" liked: "喜欢的图片"
like: "喜欢" like: "喜欢"
unlike: "取消喜欢" unlike: "取消"
_email: _email:
_follow: _follow:
title: "你有新的关注者" title: "你有新的关注者"
@ -1280,7 +1278,7 @@ _pages:
viewSource: "查看源代码" viewSource: "查看源代码"
viewPage: "查看页面" viewPage: "查看页面"
like: "赞" like: "赞"
unlike: "取消喜欢" unlike: "取消"
my: "我的页面" my: "我的页面"
liked: "喜欢的页面" liked: "喜欢的页面"
featured: "热门" featured: "热门"

View File

@ -44,7 +44,7 @@ copyLink: "複製連結"
delete: "刪除" delete: "刪除"
deleteAndEdit: "刪除並編輯" deleteAndEdit: "刪除並編輯"
deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有情感、轉發和回覆也將會消失。" deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有情感、轉發和回覆也將會消失。"
addToList: "加入至清單" addToList: "新增至清單"
sendMessage: "發送訊息" sendMessage: "發送訊息"
copyUsername: "複製用戶名" copyUsername: "複製用戶名"
searchUser: "搜尋用戶" searchUser: "搜尋用戶"
@ -129,7 +129,7 @@ customEmojis: "自訂表情符號"
emoji: "表情符號" emoji: "表情符號"
emojiName: "表情符號名稱" emojiName: "表情符號名稱"
emojiUrl: "表情符號URL" emojiUrl: "表情符號URL"
addEmoji: "加入表情符號" addEmoji: "新增表情符號"
settingGuide: "推薦設定" settingGuide: "推薦設定"
cacheRemoteFiles: "緩存非遠程檔案" cacheRemoteFiles: "緩存非遠程檔案"
cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間,但資料會因直接連線從而產生額外連接數據。" cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間,但資料會因直接連線從而產生額外連接數據。"
@ -218,7 +218,7 @@ newPasswordRetype: "確認密碼"
attachFile: "上傳附件" attachFile: "上傳附件"
more: "更多!" more: "更多!"
featured: "精選" featured: "精選"
usernameOrUserId: "使用者名稱或使用者ID" usernameOrUserId: "使用者名稱或用戶ID"
noSuchUser: "使用者不存在" noSuchUser: "使用者不存在"
lookup: "查詢" lookup: "查詢"
announcements: "公告" announcements: "公告"
@ -273,7 +273,7 @@ folderName: "資料夾名稱"
createFolder: "新增資料夾" createFolder: "新增資料夾"
renameFolder: "重新命名資料夾" renameFolder: "重新命名資料夾"
deleteFolder: "刪除資料夾" deleteFolder: "刪除資料夾"
addFile: "附件" addFile: "加附件"
emptyDrive: "雲端硬碟為空" emptyDrive: "雲端硬碟為空"
emptyFolder: "資料夾為空" emptyFolder: "資料夾為空"
unableToDelete: "無法刪除" unableToDelete: "無法刪除"
@ -693,7 +693,6 @@ editCode: "編輯代碼"
apply: "套用" apply: "套用"
receiveAnnouncementFromInstance: "接收由本實例發出的電郵通知" receiveAnnouncementFromInstance: "接收由本實例發出的電郵通知"
emailNotification: "郵件通知" emailNotification: "郵件通知"
publish: "發佈"
inChannelSearch: "頻道内搜尋" inChannelSearch: "頻道内搜尋"
useReactionPickerForContextMenu: "點擊右鍵開啟回應工具欄" useReactionPickerForContextMenu: "點擊右鍵開啟回應工具欄"
typingUsers: "{users}輸入中..." typingUsers: "{users}輸入中..."
@ -731,8 +730,6 @@ switch: "切換"
noMaintainerInformationWarning: "尚未設定管理員信息。" noMaintainerInformationWarning: "尚未設定管理員信息。"
noBotProtectionWarning: "尚未設定Bot防護。" noBotProtectionWarning: "尚未設定Bot防護。"
configure: "設定" configure: "設定"
expiration: "期限"
middle: "中"
_gallery: _gallery:
unlike: "收回喜歡" unlike: "收回喜歡"
_email: _email:
@ -1476,7 +1473,7 @@ _notification:
reply: "回覆" reply: "回覆"
renote: "轉發貼文" renote: "轉發貼文"
quote: "引用" quote: "引用"
reaction: "反應" reaction: "情感"
pollVote: "統計已投票數" pollVote: "統計已投票數"
receiveFollowRequest: "已收到追隨請求" receiveFollowRequest: "已收到追隨請求"
followRequestAccepted: "追隨請求已接受" followRequestAccepted: "追隨請求已接受"

View File

@ -1,20 +0,0 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class passwordReset1619942102890 implements MigrationInterface {
name = 'passwordReset1619942102890'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "password_reset_request" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(256) NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "PK_fcf4b02eae1403a2edaf87fd074" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0b575fa9a4cfe638a925949285" ON "password_reset_request" ("token") `);
await queryRunner.query(`CREATE INDEX "IDX_4bb7fd4a34492ae0e6cc8d30ac" ON "password_reset_request" ("userId") `);
await queryRunner.query(`ALTER TABLE "password_reset_request" ADD CONSTRAINT "FK_4bb7fd4a34492ae0e6cc8d30ac8" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "password_reset_request" DROP CONSTRAINT "FK_4bb7fd4a34492ae0e6cc8d30ac8"`);
await queryRunner.query(`DROP INDEX "IDX_4bb7fd4a34492ae0e6cc8d30ac"`);
await queryRunner.query(`DROP INDEX "IDX_0b575fa9a4cfe638a925949285"`);
await queryRunner.query(`DROP TABLE "password_reset_request"`);
}
}

View File

@ -1,18 +0,0 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class ad1620019354680 implements MigrationInterface {
name = 'ad1620019354680'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "ad" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "place" character varying(32) NOT NULL, "priority" character varying(32) NOT NULL, "url" character varying(1024) NOT NULL, "imageUrl" character varying(1024) NOT NULL, "memo" character varying(8192) NOT NULL, CONSTRAINT "PK_0193d5ef09746e88e9ea92c634d" PRIMARY KEY ("id")); COMMENT ON COLUMN "ad"."createdAt" IS 'The created date of the Ad.'; COMMENT ON COLUMN "ad"."expiresAt" IS 'The expired date of the Ad.'`);
await queryRunner.query(`CREATE INDEX "IDX_1129c2ef687fc272df040bafaa" ON "ad" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_2da24ce20ad209f1d9dc032457" ON "ad" ("expiresAt") `);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "IDX_2da24ce20ad209f1d9dc032457"`);
await queryRunner.query(`DROP INDEX "IDX_1129c2ef687fc272df040bafaa"`);
await queryRunner.query(`DROP TABLE "ad"`);
}
}

View File

@ -1,11 +1,11 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>", "author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.80.3", "version": "12.79.3",
"codename": "indigo", "codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.skeh.site/skeh/misskey.git" "url": "https://github.com/misskey-dev/misskey.git"
}, },
"main": "./index.js", "main": "./index.js",
"private": true, "private": true,
@ -32,6 +32,7 @@
"resolutions": { "resolutions": {
"chokidar": "^3.3.1", "chokidar": "^3.3.1",
"constantinople": "^4.0.1", "constantinople": "^4.0.1",
"gulp/gulp-cli/yargs/yargs-parser": "5.0.0-security.0",
"jsonld/rdf-canonize/node-forge": "0.10.0", "jsonld/rdf-canonize/node-forge": "0.10.0",
"lodash": "^4.17.20" "lodash": "^4.17.20"
}, },
@ -255,7 +256,6 @@
"xev": "2.0.1" "xev": "2.0.1"
}, },
"devDependencies": { "devDependencies": {
"@redocly/openapi-core": "1.0.0-beta.44",
"@types/chai": "4.2.16", "@types/chai": "4.2.16",
"@types/fluent-ffmpeg": "2.1.17", "@types/fluent-ffmpeg": "2.1.17",
"chai": "4.3.4", "chai": "4.3.4",

View File

@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, h, TransitionGroup } from 'vue'; import { defineComponent, h, TransitionGroup } from 'vue';
import MkAd from '@client/components/global/ad.vue';
export default defineComponent({ export default defineComponent({
props: { props: {
@ -23,11 +22,6 @@ export default defineComponent({
required: false, required: false,
default: false default: false
}, },
ad: {
type: Boolean,
required: false,
default: false
},
}, },
methods: { methods: {
@ -64,7 +58,11 @@ export default defineComponent({
if ( if (
i != this.items.length - 1 && i != this.items.length - 1 &&
new Date(item.createdAt).getDate() != new Date(this.items[i + 1].createdAt).getDate() new Date(item.createdAt).getDate() != new Date(this.items[i + 1].createdAt).getDate() &&
!item._prId_ &&
!this.items[i + 1]._prId_ &&
!item._featuredId_ &&
!this.items[i + 1]._featuredId_
) { ) {
const separator = h('div', { const separator = h('div', {
class: 'separator', class: 'separator',
@ -88,15 +86,7 @@ export default defineComponent({
return [el, separator]; return [el, separator];
} else { } else {
if (this.ad && item._shouldInsertAd_) { return el;
return [h(MkAd, {
class: 'a', // advertise()
key: item.id + ':ad',
prefer: ['horizontal', 'horizontal-big'],
}), el];
} else {
return el;
}
} }
})); }));
}, },
@ -105,10 +95,6 @@ export default defineComponent({
<style lang="scss"> <style lang="scss">
.sqadhkmv { .sqadhkmv {
> *:empty {
display: none;
}
> *:not(:last-child) { > *:not(:last-child) {
margin-bottom: var(--margin); margin-bottom: var(--margin);
} }

View File

@ -35,7 +35,6 @@
class="_button" class="_button"
@click="chosen(emoji, $event)" @click="chosen(emoji, $event)"
tabindex="0" tabindex="0"
:key="emoji"
> >
<MkEmoji :emoji="emoji" :normal="true"/> <MkEmoji :emoji="emoji" :normal="true"/>
</button> </button>
@ -105,7 +104,7 @@ export default defineComponent({
return { return {
emojilist: markRaw(emojilist), emojilist: markRaw(emojilist),
getStaticImageUrl, getStaticImageUrl,
pinned: this.$store.reactiveState.reactions, pinned: this.$store.state.reactions,
width: this.asReactionPicker ? this.$store.state.reactionPickerWidth : 3, width: this.asReactionPicker ? this.$store.state.reactionPickerWidth : 3,
height: this.asReactionPicker ? this.$store.state.reactionPickerHeight : 2, height: this.asReactionPicker ? this.$store.state.reactionPickerHeight : 2,
big: this.asReactionPicker ? isDeviceTouch : false, big: this.asReactionPicker ? isDeviceTouch : false,

View File

@ -1,71 +0,0 @@
<template>
<XModalWindow ref="dialog"
:width="370"
:height="400"
@close="$refs.dialog.close()"
@closed="$emit('closed')"
>
<template #header>{{ $ts.forgotPassword }}</template>
<form class="_monolithic_" @submit.prevent="onSubmit" v-if="$instance.enableEmail">
<div class="_section">
<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required>
<span>{{ $ts.username }}</span>
<template #prefix>@</template>
</MkInput>
<MkInput v-model:value="email" type="email" spellcheck="false" required>
<span>{{ $ts.emailAddress }}</span>
<template #desc>{{ $ts._forgotPassword.enterEmail }}</template>
</MkInput>
<MkButton type="submit" :disabled="processing" primary style="margin: 0 auto;">{{ $ts.send }}</MkButton>
</div>
<div class="_section">
<MkA to="/about" class="_link">{{ $ts._forgotPassword.ifNoEmail }}</MkA>
</div>
</form>
<div v-else>
{{ $ts._forgotPassword.contactAdmin }}
</div>
</XModalWindow>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XModalWindow from '@client/components/ui/modal-window.vue';
import MkButton from '@client/components/ui/button.vue';
import MkInput from '@client/components/ui/input.vue';
import * as os from '@client/os';
export default defineComponent({
components: {
XModalWindow,
MkButton,
MkInput,
},
emits: ['done', 'closed'],
data() {
return {
username: '',
email: '',
processing: false,
};
},
methods: {
async onSubmit() {
this.processing = true;
await os.apiWithDialog('request-reset-password', {
username: this.username,
email: this.email,
});
this.$emit('done');
this.$refs.dialog.close();
}
}
});
</script>

View File

@ -1,163 +0,0 @@
<template>
<div class="qiivuoyo" v-if="ad">
<div class="main" :class="ad.place" v-if="!showMenu">
<a :href="ad.url" target="_blank">
<img :src="ad.imageUrl">
<button class="_button menu" @click.prevent.stop="toggleMenu"><span class="fas fa-info-circle"></span></button>
</a>
</div>
<div class="menu" v-else>
<div class="body">
<div>Ads by {{ host }}</div>
<!--<MkButton>{{ $ts.stopThisAd }}</MkButton>-->
<button class="_textButton" @click="toggleMenu">{{ $ts.close }}</button>
</div>
</div>
</div>
<div v-else></div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { instance } from '@client/instance';
import { host } from '@client/config';
import MkButton from '@client/components/ui/button.vue';
export default defineComponent({
components: {
MkButton
},
props: {
prefer: {
type: Array,
required: true
},
specify: {
type: Object,
required: false
},
},
setup(props) {
const showMenu = ref(false);
const toggleMenu = () => {
showMenu.value = !showMenu.value;
};
let ad = null;
if (props.specify) {
ad = props.specify;
} else {
let ads = instance.ads.filter(ad => props.prefer.includes(ad.place));
if (ads.length === 0) {
ads = instance.ads.filter(ad => ad.place === 'square');
}
const high = ads.filter(ad => ad.priority === 'high');
const middle = ads.filter(ad => ad.priority === 'middle');
const low = ads.filter(ad => ad.priority === 'low');
if (high.length > 0) {
ad = high[Math.floor(Math.random() * high.length)];
} else if (middle.length > 0) {
ad = middle[Math.floor(Math.random() * middle.length)];
} else if (low.length > 0) {
ad = low[Math.floor(Math.random() * low.length)];
}
}
return {
ad,
showMenu,
toggleMenu,
host,
};
}
});
</script>
<style lang="scss" scoped>
.qiivuoyo {
background-size: auto auto;
background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--ad) 8px, var(--ad) 14px );
> .main {
text-align: center;
> a {
display: inline-block;
position: relative;
vertical-align: bottom;
&:hover {
> img {
filter: contrast(120%);
}
}
> img {
display: block;
object-fit: contain;
margin: auto;
}
> .menu {
position: absolute;
top: 0;
right: 0;
background: var(--panel);
}
}
&.square {
> a ,
> a > img {
max-width: min(300px, 100%);
max-height: 300px;
}
}
&.horizontal {
padding: 8px;
> a ,
> a > img {
max-width: min(600px, 100%);
max-height: 80px;
}
}
&.horizontal-big {
padding: 8px;
> a ,
> a > img {
max-width: min(600px, 100%);
max-height: 250px;
}
}
&.vertical {
> a ,
> a > img {
max-width: min(100px, 100%);
}
}
}
> .menu {
padding: 8px;
text-align: center;
> .body {
padding: 8px;
margin: 0 auto;
max-width: 400px;
border: solid 1px var(--divider);
}
}
}
</style>

View File

@ -113,6 +113,8 @@ export default defineComponent({
> .icon { > .icon {
padding-left: 2px; padding-left: 2px;
font-size: .9em; font-size: .9em;
font-weight: 400;
font-style: normal;
} }
> .self { > .self {

View File

@ -12,10 +12,8 @@ import url from './global/url.vue';
import i18n from './global/i18n'; import i18n from './global/i18n';
import loading from './global/loading.vue'; import loading from './global/loading.vue';
import error from './global/error.vue'; import error from './global/error.vue';
import ad from './global/ad.vue';
export default function(app: App) { export default function(app: App) {
app.component('I18n', i18n);
app.component('Mfm', mfm); app.component('Mfm', mfm);
app.component('MkA', a); app.component('MkA', a);
app.component('MkAcct', acct); app.component('MkAcct', acct);
@ -27,5 +25,5 @@ export default function(app: App) {
app.component('MkUrl', url); app.component('MkUrl', url);
app.component('MkLoading', loading); app.component('MkLoading', loading);
app.component('MkError', error); app.component('MkError', error);
app.component('MkAd', ad); app.component('I18n', i18n);
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="yohlumlk" v-size="{ min: [350, 500] }"> <div class="yohlumlk">
<MkAvatar class="avatar" :user="note.user"/> <MkAvatar class="avatar" :user="note.user"/>
<div class="main"> <div class="main">
<XNoteHeader class="header" :note="note" :mini="true"/> <XNoteHeader class="header" :note="note" :mini="true"/>
@ -50,19 +50,18 @@ export default defineComponent({
display: flex; display: flex;
margin: 0; margin: 0;
padding: 0; padding: 0;
overflow: clip; overflow: hidden;
font-size: 0.95em; font-size: 0.95em;
&.min-width_350px { > .avatar {
> .avatar {
@media (min-width: 350px) {
margin: 0 10px 0 0; margin: 0 10px 0 0;
width: 44px; width: 44px;
height: 44px; height: 44px;
} }
}
&.min-width_500px { @media (min-width: 500px) {
> .avatar {
margin: 0 12px 0 0; margin: 0 12px 0 0;
width: 48px; width: 48px;
height: 48px; height: 48px;

View File

@ -17,7 +17,7 @@
</MkButton> </MkButton>
</div> </div>
<XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed" :no-gap="noGap" :ad="true"> <XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed" :no-gap="noGap">
<XNote :note="note" class="_block" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/> <XNote :note="note" class="_block" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
</XList> </XList>

View File

@ -11,7 +11,6 @@
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required> <MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
<span>{{ $ts.password }}</span> <span>{{ $ts.password }}</span>
<template #prefix><i class="fas fa-lock"></i></template> <template #prefix><i class="fas fa-lock"></i></template>
<template #desc><button class="_textButton" @click="resetPassword">{{ $ts.forgotPassword }}</button></template>
</MkInput> </MkInput>
<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton> <MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
</div> </div>
@ -50,8 +49,8 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { toUnicode } from 'punycode/'; import { toUnicode } from 'punycode/';
import MkButton from '@client/components/ui/button.vue'; import MkButton from './ui/button.vue';
import MkInput from '@client/components/ui/input.vue'; import MkInput from './ui/input.vue';
import { apiUrl, host } from '@client/config'; import { apiUrl, host } from '@client/config';
import { byteify, hexify } from '@client/scripts/2fa'; import { byteify, hexify } from '@client/scripts/2fa';
import * as os from '@client/os'; import * as os from '@client/os';
@ -198,11 +197,6 @@ export default defineComponent({
this.signing = false; this.signing = false;
}); });
} }
},
resetPassword() {
os.popup(import('@client/components/forgot-password.vue'), {}, {
}, 'closed');
} }
} }
}); });

View File

@ -14,7 +14,7 @@
{{ $ts._aboutMisskey.about }} {{ $ts._aboutMisskey.about }}
</section> </section>
<FormGroup> <FormGroup>
<FormLink to="https://git.skeh.site/skeh/misskey" external> <FormLink to="https://github.com/misskey-dev/misskey" external>
<template #icon><i class="fas fa-code"></i></template> <template #icon><i class="fas fa-code"></i></template>
{{ $ts._aboutMisskey.source }} {{ $ts._aboutMisskey.source }}
<template #suffix>GitHub</template> <template #suffix>GitHub</template>

View File

@ -3,7 +3,7 @@
<div class="title">{{ title }}</div> <div class="title">{{ title }}</div>
<div class="body" v-html="body"></div> <div class="body" v-html="body"></div>
<div class="footer"> <div class="footer">
<MkLink :url="`https://git.skeh.site/skeh/misskey/blob/master/src/docs/${lang}/${doc}.md`" class="at">{{ $ts.docSource }}</MkLink> <MkLink :url="`https://github.com/misskey-dev/misskey/blob/master/src/docs/${lang}/${doc}.md`" class="at">{{ $ts.docSource }}</MkLink>
</div> </div>
</div> </div>
</template> </template>

View File

@ -33,7 +33,6 @@
<MkFollowButton v-if="!$i || $i.id != post.user.id" :user="post.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> <MkFollowButton v-if="!$i || $i.id != post.user.id" :user="post.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
</div> </div>
</div> </div>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
<MkContainer :max-height="300" :foldable="true" class="other"> <MkContainer :max-height="300" :foldable="true" class="other">
<template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template> <template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template>
<MkPagination :pagination="otherPostsPagination" #default="{items}"> <MkPagination :pagination="otherPostsPagination" #default="{items}">

View File

@ -1,126 +0,0 @@
<template>
<div class="uqshojas">
<MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
<section class="_card _gap ads" v-for="ad in ads">
<div class="_content ad">
<MkAd v-if="ad.url" :specify="ad"/>
<MkInput v-model:value="ad.url" type="url">
<span>URL</span>
</MkInput>
<MkInput v-model:value="ad.imageUrl">
<span>{{ $ts.imageUrl }}</span>
</MkInput>
<div style="margin: 32px 0;">
<MkRadio v-model="ad.place" value="square">square</MkRadio>
<MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio>
<MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio>
</div>
<div style="margin: 32px 0;">
{{ $ts.priority }}
<MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
<MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
</div>
<MkInput v-model:value="ad.expiresAt" type="date">
<span>{{ $ts.expiration }}</span>
</MkInput>
<MkTextarea v-model:value="ad.memo">
<span>{{ $ts.memo }}</span>
</MkTextarea>
<div class="buttons">
<MkButton class="button" inline @click="save(ad)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
<MkButton class="button" inline @click="remove(ad)" danger><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton>
</div>
</div>
</section>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import MkButton from '@client/components/ui/button.vue';
import MkInput from '@client/components/ui/input.vue';
import MkTextarea from '@client/components/ui/textarea.vue';
import MkRadio from '@client/components/ui/radio.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
export default defineComponent({
components: {
MkButton,
MkInput,
MkTextarea,
MkRadio,
},
emits: ['info'],
data() {
return {
[symbols.PAGE_INFO]: {
title: this.$ts.ads,
icon: 'fas fa-audio-description'
},
ads: [],
}
},
created() {
os.api('admin/ad/list').then(ads => {
this.ads = ads;
});
},
mounted() {
this.$emit('info', this[symbols.PAGE_INFO]);
},
methods: {
add() {
this.ads.unshift({
id: null,
memo: '',
place: 'square',
priority: 'middle',
url: '',
imageUrl: null,
expiresAt: null,
});
},
remove(ad) {
os.dialog({
type: 'warning',
text: this.$t('removeAreYouSure', { x: ad.url }),
showCancelButton: true
}).then(({ canceled }) => {
if (canceled) return;
this.ads = this.ads.filter(x => x != ad);
os.apiWithDialog('admin/ad/delete', {
id: ad.id
});
});
},
save(ad) {
if (ad.id == null) {
os.apiWithDialog('admin/ad/create', {
...ad,
expiresAt: new Date(ad.expiresAt).getTime()
});
} else {
os.apiWithDialog('admin/ad/update', {
...ad,
expiresAt: new Date(ad.expiresAt).getTime()
});
}
}
}
});
</script>
<style lang="scss" scoped>
.uqshojas {
margin: var(--margin);
}
</style>

View File

@ -23,7 +23,6 @@
<FormLink :active="page === 'queue'" replace to="/instance/queue"><template #icon><i class="fas fa-clipboard-list"></i></template>{{ $ts.jobQueue }}</FormLink> <FormLink :active="page === 'queue'" replace to="/instance/queue"><template #icon><i class="fas fa-clipboard-list"></i></template>{{ $ts.jobQueue }}</FormLink>
<FormLink :active="page === 'files'" replace to="/instance/files"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink> <FormLink :active="page === 'files'" replace to="/instance/files"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink>
<FormLink :active="page === 'announcements'" replace to="/instance/announcements"><template #icon><i class="fas fa-broadcast-tower"></i></template>{{ $ts.announcements }}</FormLink> <FormLink :active="page === 'announcements'" replace to="/instance/announcements"><template #icon><i class="fas fa-broadcast-tower"></i></template>{{ $ts.announcements }}</FormLink>
<FormLink :active="page === 'ads'" replace to="/instance/ads"><template #icon><i class="fas fa-audio-description"></i></template>{{ $ts.ads }}</FormLink>
<FormLink :active="page === 'abuses'" replace to="/instance/abuses"><template #icon><i class="fas fa-exclamation-circle"></i></template>{{ $ts.abuseReports }}</FormLink> <FormLink :active="page === 'abuses'" replace to="/instance/abuses"><template #icon><i class="fas fa-exclamation-circle"></i></template>{{ $ts.abuseReports }}</FormLink>
</FormGroup> </FormGroup>
<FormGroup> <FormGroup>
@ -103,7 +102,6 @@ export default defineComponent({
case 'queue': return defineAsyncComponent(() => import('./queue.vue')); case 'queue': return defineAsyncComponent(() => import('./queue.vue'));
case 'files': return defineAsyncComponent(() => import('./files.vue')); case 'files': return defineAsyncComponent(() => import('./files.vue'));
case 'announcements': return defineAsyncComponent(() => import('./announcements.vue')); case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
case 'ads': return defineAsyncComponent(() => import('./ads.vue'));
case 'database': return defineAsyncComponent(() => import('./database.vue')); case 'database': return defineAsyncComponent(() => import('./database.vue'));
case 'abuses': return defineAsyncComponent(() => import('./abuses.vue')); case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
case 'settings': return defineAsyncComponent(() => import('./settings.vue')); case 'settings': return defineAsyncComponent(() => import('./settings.vue'));

View File

@ -45,7 +45,6 @@
<div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> <div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
<div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> <div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
</div> </div>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
<MkContainer :max-height="300" :foldable="true" class="other"> <MkContainer :max-height="300" :foldable="true" class="other">
<template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template> <template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template>
<MkPagination :pagination="otherPostsPagination" #default="{items}"> <MkPagination :pagination="otherPostsPagination" #default="{items}">

View File

@ -1,69 +0,0 @@
<template>
<FormBase v-if="token">
<FormInput v-model:value="password" type="password">
<template #prefix><i class="fas fa-lock"></i></template>
<span>{{ $ts.newPassword }}</span>
</FormInput>
<FormButton primary @click="save">{{ $ts.save }}</FormButton>
</FormBase>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import FormLink from '@client/components/form/link.vue';
import FormBase from '@client/components/form/base.vue';
import FormGroup from '@client/components/form/group.vue';
import FormInput from '@client/components/form/input.vue';
import FormButton from '@client/components/form/button.vue';
import * as os from '@client/os';
import * as symbols from '@client/symbols';
export default defineComponent({
components: {
FormBase,
FormGroup,
FormLink,
FormInput,
FormButton,
},
props: {
token: {
type: String,
required: false
}
},
data() {
return {
[symbols.PAGE_INFO]: {
title: this.$ts.resetPassword,
icon: 'fas fa-lock'
},
password: '',
}
},
mounted() {
if (this.token == null) {
os.popup(import('@client/components/forgot-password.vue'), {}, {}, 'closed');
this.$router.push('/');
}
},
methods: {
async save() {
await os.apiWithDialog('reset-password', {
token: this.token,
password: this.password,
});
this.$router.push('/');
}
}
});
</script>
<style lang="scss" scoped>
</style>

View File

@ -10,7 +10,6 @@
</div> </div>
<FormLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</FormLink> <FormLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</FormLink>
</FormGroup> </FormGroup>
<FormInfo v-if="emailNotConfigured" warn>{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></FormInfo>
<FormGroup> <FormGroup>
<template #label>{{ $ts.basicSettings }}</template> <template #label>{{ $ts.basicSettings }}</template>
<FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</FormLink> <FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</FormLink>
@ -59,13 +58,10 @@ import FormLink from '@client/components/form/link.vue';
import FormGroup from '@client/components/form/group.vue'; import FormGroup from '@client/components/form/group.vue';
import FormBase from '@client/components/form/base.vue'; import FormBase from '@client/components/form/base.vue';
import FormButton from '@client/components/form/button.vue'; import FormButton from '@client/components/form/button.vue';
import FormInfo from '@client/components/form/info.vue';
import { scroll } from '@client/scripts/scroll'; import { scroll } from '@client/scripts/scroll';
import { signout } from '@client/account'; import { signout } from '@client/account';
import { unisonReload } from '@client/scripts/unison-reload'; import { unisonReload } from '@client/scripts/unison-reload';
import * as symbols from '@client/symbols'; import * as symbols from '@client/symbols';
import { instance } from '@client/instance';
import { $i } from '@client/account';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -73,7 +69,6 @@ export default defineComponent({
FormLink, FormLink,
FormGroup, FormGroup,
FormButton, FormButton,
FormInfo,
}, },
props: { props: {
@ -178,8 +173,6 @@ export default defineComponent({
} }
}); });
const emailNotConfigured = computed(() => instance.enableEmail && ($i.email == null || !$i.emailVerified));
return { return {
[symbols.PAGE_INFO]: INFO, [symbols.PAGE_INFO]: INFO,
page, page,
@ -189,7 +182,6 @@ export default defineComponent({
onInfo, onInfo,
pageProps, pageProps,
component, component,
emailNotConfigured,
logout: () => { logout: () => {
signout(); signout();
}, },

View File

@ -70,8 +70,6 @@ export default defineComponent({
border-radius: var(--radius); border-radius: var(--radius);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
overflow: hidden; overflow: hidden;
max-width: 500px;
margin: 32px auto;
> h1 { > h1 {
margin: 0; margin: 0;

View File

@ -23,7 +23,6 @@ export const router = createRouter({
{ path: '/@:user/pages/:pageName/view-source', component: page('page-editor/page-editor'), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) }, { path: '/@:user/pages/:pageName/view-source', component: page('page-editor/page-editor'), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) },
{ path: '/@:acct/room', props: true, component: page('room/room') }, { path: '/@:acct/room', props: true, component: page('room/room') },
{ path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) }, { path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) },
{ path: '/reset-password/:token?', component: page('reset-password'), props: route => ({ token: route.params.token }) },
{ path: '/announcements', component: page('announcements') }, { path: '/announcements', component: page('announcements') },
{ path: '/about', component: page('about') }, { path: '/about', component: page('about') },
{ path: '/about-misskey', component: page('about-misskey') }, { path: '/about-misskey', component: page('about-misskey') },

View File

@ -91,10 +91,8 @@ export default (opts) => ({
...params, ...params,
limit: this.pagination.noPaging ? (this.pagination.limit || 10) : (this.pagination.limit || 10) + 1, limit: this.pagination.noPaging ? (this.pagination.limit || 10) : (this.pagination.limit || 10) + 1,
}).then(items => { }).then(items => {
for (let i = 0; i < items.length; i++) { for (const item of items) {
const item = items[i];
markRaw(item); markRaw(item);
if (i === 3) item._shouldInsertAd_ = true;
} }
if (!this.pagination.noPaging && (items.length > (this.pagination.limit || 10))) { if (!this.pagination.noPaging && (items.length > (this.pagination.limit || 10))) {
items.pop(); items.pop();
@ -130,10 +128,8 @@ export default (opts) => ({
untilId: this.pagination.reversed ? this.items[0].id : this.items[this.items.length - 1].id, untilId: this.pagination.reversed ? this.items[0].id : this.items[this.items.length - 1].id,
}), }),
}).then(items => { }).then(items => {
for (let i = 0; i < items.length; i++) { for (const item of items) {
const item = items[i];
markRaw(item); markRaw(item);
if (i === 10) item._shouldInsertAd_ = true;
} }
if (items.length > SECOND_FETCH_LIMIT) { if (items.length > SECOND_FETCH_LIMIT) {
items.pop(); items.pop();

View File

@ -11,8 +11,6 @@
@media (max-width: 500px) { @media (max-width: 500px) {
--margin: var(--marginHalf); --margin: var(--marginHalf);
} }
//--ad: rgb(255 169 0 / 10%);
} }
::selection { ::selection {
@ -339,7 +337,7 @@ hr {
} }
._monolithic_ { ._monolithic_ {
._section:not(:empty) { ._section {
box-sizing: border-box; box-sizing: border-box;
padding: var(--root-margin, 32px); padding: var(--root-margin, 32px);

View File

@ -42,7 +42,11 @@ export default defineComponent({
if ( if (
i != this.items.length - 1 && i != this.items.length - 1 &&
new Date(item.createdAt).getDate() != new Date(this.items[i + 1].createdAt).getDate() new Date(item.createdAt).getDate() != new Date(this.items[i + 1].createdAt).getDate() &&
!item._prId_ &&
!this.items[i + 1]._prId_ &&
!item._featuredId_ &&
!this.items[i + 1]._featuredId_
) { ) {
const separator = h('div', { const separator = h('div', {
class: 'separator', class: 'separator',

View File

@ -313,7 +313,7 @@ export default defineComponent({
} }
}; };
if (isLink(e.target)) return; if (isLink(e.target)) return;
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return; if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return;
if (window.getSelection().toString() !== '') return; if (window.getSelection().toString() !== '') return;
const path = this.$route.path; const path = this.$route.path;
os.contextMenu([{ os.contextMenu([{

View File

@ -64,7 +64,7 @@ export default defineComponent({
} }
}; };
if (isLink(e.target)) return; if (isLink(e.target)) return;
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return; if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return;
if (window.getSelection().toString() !== '') return; if (window.getSelection().toString() !== '') return;
const path = this.$route.path; const path = this.$route.path;
os.contextMenu([{ os.contextMenu([{

View File

@ -165,7 +165,7 @@ export default defineComponent({
} }
}; };
if (isLink(e.target)) return; if (isLink(e.target)) return;
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return; if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return;
if (window.getSelection().toString() !== '') return; if (window.getSelection().toString() !== '') return;
const path = this.$route.path; const path = this.$route.path;
os.contextMenu([{ os.contextMenu([{

View File

@ -1,7 +1,6 @@
<template> <template>
<div class="ddiqwdnk"> <div class="efzpzdvf">
<XWidgets class="widgets" :edit="editMode" :widgets="$store.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/> <XWidgets class="widgets" :edit="editMode" :widgets="$store.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
<MkAd class="a" prefer="square"/>
<button v-if="editMode" @click="editMode = false" class="_textButton edit" style="font-size: 0.9em;"><i class="fas fa-check"></i> {{ $ts.editWidgetsExit }}</button> <button v-if="editMode" @click="editMode = false" class="_textButton edit" style="font-size: 0.9em;"><i class="fas fa-check"></i> {{ $ts.editWidgetsExit }}</button>
<button v-else @click="editMode = true" class="_textButton edit" style="font-size: 0.9em;"><i class="fas fa-pencil-alt"></i> {{ $ts.editWidgets }}</button> <button v-else @click="editMode = true" class="_textButton edit" style="font-size: 0.9em;"><i class="fas fa-pencil-alt"></i> {{ $ts.editWidgets }}</button>
@ -57,14 +56,13 @@ export default defineComponent({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.ddiqwdnk { .efzpzdvf {
position: sticky; position: sticky;
height: min-content; height: min-content;
box-sizing: border-box; box-sizing: border-box;
padding-bottom: 8px; padding-bottom: 8px;
> .widgets, > .widgets {
> .a {
width: 300px; width: 300px;
} }

View File

@ -191,7 +191,7 @@ export default defineComponent({
} }
}; };
if (isLink(e.target)) return; if (isLink(e.target)) return;
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return; if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO'].includes(e.target.tagName) || e.target.attributes['contenteditable']) return;
if (window.getSelection().toString() !== '') return; if (window.getSelection().toString() !== '') return;
const path = this.$route.path; const path = this.$route.path;
os.contextMenu([{ os.contextMenu([{

View File

@ -32,7 +32,7 @@
</main> </main>
<div class="powered-by"> <div class="powered-by">
<b><MkA to="/">{{ host }}</MkA></b> <b><MkA to="/">{{ host }}</MkA></b>
<small>Powered by <a href="https://git.skeh.site/skeh/misskey" target="_blank">Misskey</a></small> <small>Powered by <a href="https://github.com/misskey-dev/misskey" target="_blank">Misskey</a></small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="mk-app"> <div class="mk-app">
<a v-if="root" href="https://git.skeh.site/skeh/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--panel); color:var(--fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a> <a v-if="root" href="https://github.com/misskey-dev/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--panel); color:var(--fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
<div class="side" v-if="!narrow && !root"> <div class="side" v-if="!narrow && !root">
<XKanban class="kanban" full/> <XKanban class="kanban" full/>
@ -20,7 +20,7 @@
</main> </main>
<div class="powered-by" v-if="!root"> <div class="powered-by" v-if="!root">
<b><MkA to="/">{{ host }}</MkA></b> <b><MkA to="/">{{ host }}</MkA></b>
<small>Powered by <a href="https://git.skeh.site/skeh/misskey" target="_blank">Misskey</a></small> <small>Powered by <a href="https://github.com/misskey-dev/misskey" target="_blank">Misskey</a></small>
</div> </div>
</div> </div>
</div> </div>

View File

@ -28,7 +28,7 @@
</div> </div>
<div class="powered-by" v-if="poweredBy"> <div class="powered-by" v-if="poweredBy">
<b><MkA to="/">{{ host }}</MkA></b> <b><MkA to="/">{{ host }}</MkA></b>
<small>Powered by <a href="https://git.skeh.site/skeh/misskey" target="_blank">Misskey</a></small> <small>Powered by <a href="https://github.com/misskey-dev/misskey" target="_blank">Misskey</a></small>
</div> </div>
</template> </template>
</div> </div>

View File

@ -70,8 +70,6 @@ import { Channel } from '../models/entities/channel';
import { ChannelFollowing } from '../models/entities/channel-following'; import { ChannelFollowing } from '../models/entities/channel-following';
import { ChannelNotePining } from '../models/entities/channel-note-pining'; import { ChannelNotePining } from '../models/entities/channel-note-pining';
import { RegistryItem } from '../models/entities/registry-item'; import { RegistryItem } from '../models/entities/registry-item';
import { Ad } from '../models/entities/ad';
import { PasswordResetRequest } from '@/models/entities/password-reset-request';
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
@ -171,8 +169,6 @@ export const entities = [
ChannelFollowing, ChannelFollowing,
ChannelNotePining, ChannelNotePining,
RegistryItem, RegistryItem,
Ad,
PasswordResetRequest,
...charts as any ...charts as any
]; ];

View File

@ -43,7 +43,7 @@ Theme codes are saved as a JSON5 object of theme options. Themes are composed of
* `props` ... The style definitions of the theme.These will be explained in the following. * `props` ... The style definitions of the theme.These will be explained in the following.
### Theme style definitions ### Theme style definitions
Define the style of the theme within `props`. The keys will become CSS variables names, and the value specifies the content. In addition, the default `props` options are inherited from the base theme. If this theme's `base` is `light`, they will be copied from [_light.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_light.json5), and if it is `dark`, they will be copied from [_dark.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_dark.json5). In other words, if there is for example no `panel` key contained in `props`, then the value of `panel` from the base theme will be used. Define the style of the theme within `props`. The keys will become CSS variables, and the value specifies the content. In addition, the default `props` options are inherited from the base theme. If this theme's `base` is `light`, they will be copied from [_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5), if it is `dark` they will be copied from [_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5). In other words, if there is for example no `panel` key contained in `props`, then the value of `panel` from the base theme will be used.
#### Syntax for values #### Syntax for values
* Hex colors * Hex colors

View File

@ -1,4 +1,4 @@
# AiScript # AiScript
## funciones ## funciones
Pasando valores por defecto デフォルトで値渡しです。

View File

@ -1,74 +1,74 @@
# Création d'un plugin # プラグインの作成
En utilisant la fonction plugin du client web Misskey, vous pouvez étendre et y ajouter de nouvelles fonctionnalités. Cette page liste la définition des métadonnées et les références de l'API AIScript pour la création des plugins. Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。
## Métadonnées ## Métadonnées
Les plugins doivent définir des métadonnées de plugin par défaut via le format de métadonnées AiScript. Les métadonnées sont un objet contenant les propriétés suivantes : プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。
### name ### name
Nom du plugin. プラグイン名
### author ### author
Nom de l'auteur du plugin. プラグイン作者
### version ### version
Version du plugin.Cette valeur doit être un nombre. プラグインバージョン。数値を指定してください。
### description ### description
Description du plugin. プラグインの説明
### permissions ### permissions
Permissions requises par le plugin.Utilisé pour les requêtes de l'API Misskey. プラグインが要求する権限。MisskeyAPIにリクエストする際に用いられます。
### config ### config
Un objet représentant les paramètres du plugin. Les clés représentent les noms des paramètres et les valeurs sont l'une des propriétés ci-dessous. プラグインの設定情報を表すオブジェクト。 キーに設定名、値に以下のプロパティを含めます。
#### type #### type
Une chaîne de caractères représentant le type de valeur du paramètre.Sélectionnez l'une des options suivantes : string number boolean 設定値の種類を表す文字列。以下から選択します。 string number boolean
#### label #### label
Nom du paramètre affiché à l'utilisateur. ユーザーに表示する設定名
#### description #### description
Description du paramètre 設定の説明
#### default #### default
Valeur par défaut du paramètre 設定のデフォルト値
## Références API de Misskey ## Références API de Misskey
L'API intégrée directement dans la norme AiScript elle-même ne sera pas répertoriée. AiScript標準で組み込まれているAPIは掲載しません。
### Mk:dialog(title text type) ### Mk:dialog(title text type)
Affiche la boîte de dialogue.type peut être défini par les valeurs suivantes. info success warn error question Si elle est omise, c'est "info" qui est utilisée. ダイアログを表示します。typeには以下の値が設定できます。 info success warn error question 省略すると info になります。
### Mk:confirm(title text type) ### Mk:confirm(title text type)
Affiche une boîte de dialogue de confirmation.Le type peut être défini par les valeurs suivantes. info success warn error question Si elle est omise, c'est "question" qui est utilisé par défaut. Si l'utilisateur sélectionne "OK", true est renvoyé, si l'utilisateur sélectionne "Cancel", false est renvoyé. 確認ダイアログを表示します。typeには以下の値が設定できます。 info success warn error question 省略すると question になります。 ユーザーが"OK"を選択した場合は true を、"キャンセル"を選択した場合は false が返ります。
### Mk:api(endpoint params) ### Mk:api(endpoint params)
Envoie une requête à l'API Misskey.Le premier paramètre spécifie le point de terminaison de l'API, le second spécifie les paramètres de la requête sous forme d'objet. Misskey APIにリクエストします。第一引数にエンドポイント名、第二引数にパラメータオブジェクトを渡します。
### Mk:save(key value) ### Mk:save(key value)
Fait persister une valeur arbitraire avec un nom arbitraire.La valeur persistante reste après la fin du contexte AiScript et peut être lue par Mk:load. 任意の値に任意の名前を付けて永続化します。永続化した値は、AiScriptコンテキストが終了しても残り、Mk:loadで読み取ることができます。
### Mk:load(key) ### Mk:load(key)
Lit la valeur du nom spécifié persisté par Mk:save. Mk:saveで永続化した指定の名前の値を読み取ります。
### Plugin:register_post_form_action(title fn) ### Plugin:register_post_form_action(title fn)
Ajoute une action au formulaire de soumission.Le premier argument est le nom de l'action, le second est la fonction de rappel lorsque l'action est sélectionnée. La fonction de rappel reçoit l'objet du formulaire de soumission comme premier argument. 投稿フォームにアクションを追加します。第一引数にアクション名、第二引数にアクションが選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に投稿フォームオブジェクトが渡されます。
### Plugin:register_note_action(title fn) ### Plugin:register_note_action(title fn)
Ajoute un élément au menu note. Le premier paramètre spécifie le nom de l'action, le second paramètre spécifie une fonction de rappel qui est exécutée lorsque cet élément est sélectionné. La fonction de rappel reçoit un objet note comme premier paramètre. ノートメニューに項目を追加します。第一引数に項目名、第二引数に項目が選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。
### Plugin:register_user_action(title fn) ### Plugin:register_user_action(title fn)
Ajoute un élément au menu de l'utilisateur.Le premier paramètre spécifie le nom de l'action, le second paramètre spécifie une fonction de rappel qui est exécutée lorsque cet élément est sélectionné. La fonction de rappel reçoit un objet utilisateur comme premier paramètre. ユーザーメニューに項目を追加します。第一引数に項目名、第二引数に項目が選択された際のコールバック関数を渡します。 コールバック関数には、第一引数に対象のユーザーオブジェクトが渡されます。
### Plugin:register_note_view_interruptor(fn) ### Plugin:register_note_view_interruptor(fn)
Réécrit les informations de la note affichée dans l'interface utilisateur. L'objet note cible est passé comme premier argument à la fonction de rappel. La note est réécrite dans la valeur de retour de la fonction de rappel. UIに表示されるート情報を書き換えます。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。 コールバック関数の返り値でノートが書き換えられます。
### Plugin:register_note_post_interruptor(fn) ### Plugin:register_note_post_interruptor(fn)
Réécrit les informations de la note lors de la publication d'une note. L'objet note cible est passé comme premier argument à la fonction de rappel. La note sera réécrite dans la valeur de retour de la fonction de rappel. ノート投稿時にノート情報を書き換えます。 コールバック関数には、第一引数に対象のノートオブジェクトが渡されます。 コールバック関数の返り値でノートが書き換えられます。
### Plugin:open_url(url) ### Plugin:open_url(url)
Ouvre l'URL passée comme premier argument dans un nouvel onglet du navigateur. 第一引数に渡されたURLをブラウザの新しいタブで開きます。
### Plugin:config ### Plugin:config
Un objet dans lequel la configuration du plugin est stockée.La valeur est saisie par la clé définie dans la configuration de la définition du plugin. プラグインの設定が格納されるオブジェクト。プラグイン定義のconfigで設定したキーで値が入ります。

View File

@ -1,33 +1,33 @@
# Développement du bot Reversi de Misskey # MisskeyリバーシBotの開発
Cette page explique comment développer un bot pour la fonction Reversi de Misskey. Misskeyのリバーシ機能に対応したBotの開発方法をここに記します。
1. Connectez-vous au flux `games/reversi` avec les paramètres suivants : 1. `games/reversi`ストリームに以下のパラメータを付けて接続する:
* `i` : Clé API pour le compte du bot * `i`: botアカウントのAPIキー
2. Lorsqu'une invitation à un jeu arrive, un événement `invited` sera lancé à partir du flux. 2. 対局への招待が来たら、ストリームから`invited`イベントが流れてくる
* Le contenu de cet événement est un attribut `parent`, qui contient des informations sur l'utilisateur qui a envoyé l'invitation. * イベントの中身に、`parent`という名前で対局へ誘ってきたユーザーの情報が含まれている
3. Envoie une requête à `games/reversi/match`, où la valeur du paramètre `user_id` est l'attribut `id` de l'objet `parent` obtenu précédemment. 3. `games/reversi/match`へ、`user_id`として`parent`の`id`が含まれたリクエストを送信する
4. Si la requête fonctionne, les informations sur le jeu seront renvoyées et vous pourrez vous connecter au flux `games/reversi-game` avec les paramètres suivants : 4. 上手くいくとゲーム情報が返ってくるので、`games/reversi-game`ストリームへ、以下のパラメータを付けて接続する:
* `i` : Clé API pour le compte du bot * `i`: botアカウントのAPIキー
* `game`: `game` de `id` * `game`: `game`の`id`
5. Pendant ce temps, l'adversaire peut modifier les paramètres du jeu. Chaque fois qu'un paramètre est modifié, le flux envoie un événement `update-settings`, donc une logique pour gérer ces événements peut être nécessaire. 5. この間、相手がゲームの設定を変更するとその都度`update-settings`イベントが流れてくるので、必要であれば何かしらの処理を行う
6. Une fois que vous êtes satisfait·e des paramètres du jeu, envoyez le message `{ type : 'accept' }` au flux. 6. 設定に満足したら、`{ type: 'accept' }`メッセージをストリームに送信する
7. Lorsque le jeu commence, l'événement `started` sera envoyé. 7. ゲームが開始すると、`started`イベントが流れてくる
* Les informations sur l'état du jeu seront inclus dans cet événement. * イベントの中身にはゲーム情報が含まれている
8. Pour placer une pierre, envoyez `{ type : 'set', pos : <Position&gt ; }` au flux (voir ci-dessous pour savoir comment calculer la position). 8. 石を打つには、ストリームに`{ type: 'set', pos: <位置> }`を送信する(位置の計算方法は後述)
9. Lorsque votre adversaire ou vous-même placez une pierre, un événement `set` est envoyé depuis le flux. 9. 相手または自分が石を打つと、ストリームから`set`イベントが流れてくる
* `color` contient la couleur de la pierre placée * `color`として石の色が含まれている
* `pos` contient la position de la pierre * `pos`として位置情報が含まれている
## Calculer la position ## 位置の計算法
Si nous considérons une carte 8x8, la position de chaque carré (appelée index) est la suivante : 8x8のマップを考える場合、各マスの位置(インデックスと呼びます)は次のようになっています:
``` ```
+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
| 0| 1| 2| 3| 4| 5| 6| 7| | 0| 1| 2| 3| 4| 5| 6| 7|
@ -38,29 +38,29 @@ Si nous considérons une carte 8x8, la position de chaque carré (appelée index
... ...
``` ```
### Trouver les index à partir des coordonnées X, Y ### X,Y座標 から インデックス に変換する
``` ```
pos = x + (y * mapWidth) pos = x + (y * mapWidth)
``` ```
`mapWidth` est une donnée de la carte prise sur la `map` comme suit : `mapWidth`は、ゲーム情報の`map`から、次のようにして計算できます:
``` ```
mapWidth = map[0].length mapWidth = map[0].length
``` ```
### Trouver les coordonnées X, Y depuis l'index ### インデックス から X,Y座標 に変換する
``` ```
x = pos % mapWidth x = pos % mapWidth
y = Math.floor(pos / mapWidth) y = Math.floor(pos / mapWidth)
``` ```
## Information sur la carte ## マップ情報
Les données de la carte sont incluses dans `map` dans les données du jeu. Comme les données sont représentées sous la forme d'un tableau de chaînes de caractères, chaque caractère représente un champ. Sur la base de ces données, vous pouvez reconstruire l'état de la carte : マップ情報は、ゲーム情報の`map`に入っています。 文字列の配列になっており、ひとつひとつの文字がマス情報を表しています。 それをもとにマップのデザインを知る事が出来ます:
* `(Vide)` ... Aucun champ * `(スペース)` ... マス無し
* `-` ... Champ * `-` ... マス
* `b` ... La première pierre placée est noire * `b` ... 初期配置される黒石
* `w` ... La première pierre placée est blanche * `w` ... 初期配置される白石
Par exemple, supposons que nous ayons la carte simple suivante de 4×4 : 例えば、4*4の次のような単純なマップがあるとします:
```text ```text
+---+---+---+---+ +---+---+---+---+
| | | | | | | | | |
@ -73,23 +73,23 @@ Par exemple, supposons que nous ayons la carte simple suivante de 4×4 :
+---+---+---+---+ +---+---+---+---+
``` ```
Dans ce cas, les données de la carte ressembleront à ceci : この場合、マップデータはこのようになります:
```javascript ```javascript
['----', '-wb-', '-bw-', '----'] ['----', '-wb-', '-bw-', '----']
``` ```
## Créer un Bot interactif en présentant un formulaire à l'utilisateur. ## ユーザーにフォームを提示して対話可能Botを作成する
Afin de communiquer avec l'utilisateur, un formulaire peut être présenté à l'utilisateur sur l'écran des paramètres du jeu. Par exemple, un scénario pourrait consister à permettre à l'utilisateur de définir la force du bot. ユーザーとのコミュニケーションを行うため、ゲームの設定画面でユーザーにフォームを提示することができます。 例えば、Botの強さをユーザーが設定できるようにする、といったシナリオが考えられます。
Pour présenter le formulaire, envoyez le message suivant au flux `reversi-game` : フォームを提示するには、`reversi-game`ストリームに次のメッセージを送信します:
```javascript ```javascript
{ {
type: 'init-form', type: 'init-form',
body: [Tableau de contrôles de formulaires] body: [フォームコントロールの配列]
} }
``` ```
Nous allons maintenant expliquer le tableau des contrôles de formulaires. Un contrôle de formulaire est un objet qui ressemble à ce qui suit : フォームコントロールの配列については今から説明します。 フォームコントロールは、次のようなオブジェクトです:
```javascript ```javascript
{ {
id: 'switch1', id: 'switch1',
@ -98,10 +98,10 @@ Nous allons maintenant expliquer le tableau des contrôles de formulaires. Un co
value: false value: false
} }
``` ```
`id` ... ID de l'élément de contrôle. `type` ... Le type d'élément de contrôle. Nous y reviendrons plus tard. Texte affiché à côté de l'élément de contrôle. `value` ... La valeur par défaut de l'élément de contrôle. `id` ... コントロールのID。 `type` ... コントロールの種類。後述します。 `label` ... コントロールと一緒に表記するテキスト。 `value` ... コントロールのデフォルト値。
### Gestion des interactions avec les formulaires ### フォームの操作を受け取る
Lorsqu'un utilisateur interagit avec le formulaire, un événement `update-form` est envoyé par le flux. Le contenu de l'événement contient l'ID du contrôle et la valeur définie par l'utilisateur. Par exemple, si l'utilisateur allume l'interrupteur illustré ci-dessus, l'événement suivant sera diffusé : ユーザーがフォームを操作すると、ストリームから`update-form`イベントが流れてきます。 イベントの中身には、コントロールのIDと、ユーザーが設定した値が含まれています。 例えば、上で示したスイッチをユーザーがオンにしたとすると、次のイベントが流れてきます:
```javascript ```javascript
{ {
id: 'switch1', id: 'switch1',
@ -109,52 +109,52 @@ Lorsqu'un utilisateur interagit avec le formulaire, un événement `update-form`
} }
``` ```
### Types d'éléments de contrôles de formulaires ### フォームコントロールの種類
#### Interrupteur #### Interrupteur
type: `switch` Affiche un interrupteur.Cette fonction est utile lorsque vous souhaitez activer ou désactiver une fonction. type: `switch` スイッチを表示します。何かの機能をオン/オフさせたい場合に有用です。
##### Propriétés ##### プロパティ
`label` ... Texte à marquer sur l'interrupteur. `label` ... スイッチに表記するテキスト。
#### Boutons radio #### ラジオボタン
type: `radio` Affiche le bouton radio.Il est utile pour proposer des options.Par exemple, pour choisir la difficulté du bot. type: `radio` ラジオボタンを表示します。選択肢を提示するのに有用です。例えば、Botの強さを設定させるなどです。
##### Propriétés ##### プロパティ
`items` ... Les options des boutons radio. Par exemple : `items` ... ラジオボタンの選択肢。例:
```javascript ```javascript
items: [{ items: [{
label: 'Facile', label: '',
value: 1 value: 1
}, { }, {
label: 'Moyen', label: '',
value: 2 value: 2
}, { }, {
label: 'Difficile', label: '',
value: 3 value: 3
}] }]
``` ```
#### Glissière #### スライダー
type: `slider` Affiche une glissière. type: `slider` スライダーを表示します。
##### Propriétés ##### プロパティ
`min` ... Limite minimum de la glissière. `max` ... Limite maximum de la glissière. `step` ... Étapes entre les valeurs de la glissière. `min` ... スライダーの下限。 `max` ... スライダーの上限。 `step` ... 入力欄で刻むステップ値。
#### Zones de texte #### テキストボックス
type: `textbox` Affiche une zone de texte.Cette fonction peut être utilisée à des fins générales lorsque vous souhaitez que l'utilisateur tape quelque chose. type: `textbox` テキストボックスを表示します。ユーザーになにか入力させる一般的な用途に利用できます。
## Afficher un message à l'utilisateur ## ユーザーにメッセージを表示する
C'est un autre moyen, autre que les formulaires, d'interagir avec les utilisateurs dans l'écran de configuration.Vous pouvez afficher un message à l'intention de l'utilisateur. Par exemple, vous pouvez afficher un avertissement lorsque l'utilisateur sélectionne un mode ou une carte qui n'est pas pris en charge par le Bot. Pour afficher un message, envoyez le message suivant au flux : 設定画面でユーザーと対話する、フォーム以外のもうひとつの方法がこれです。ユーザーになにかメッセージを表示することができます。 例えば、ユーザーがBotの対応していないモードやマップを選択したとき、警告を表示するなどです。 メッセージを表示するには、次のメッセージをストリームに送信します:
```javascript ```javascript
{ {
type: 'message', type: 'message',
body: { body: {
text: 'contenu du message', text: 'メッセージ内容',
type: 'Type du message' type: 'メッセージの種類'
} }
} }
``` ```
Type de message : `success`, `info`, `warning`, `error`. メッセージの種類: `success`, `info`, `warning`, `error`
## Abandonner ## 投了する
Pour se rendre, faites une demande à <a href="./api/endpoints/games/reversi/games/surrender">cette terminaison</a>. 投了をするには、<a href="./api/endpoints/games/reversi/games/surrender">このエンドポイント</a>にリクエストします。

View File

@ -1,25 +1,25 @@
# API streaming # API Stream
L'API Streaming permet d'implémenter l'exécution d'opérations variées et la réception de diverses informations en temps réel. Cela concerne, par exemple, l'affichage des nouvelles publications dans les fils, la réception de nouveaux messages, les nouveaux abonnements, etc. L'API Stream permet d'implémenter l'exécution d'opérations variées et la réception de diverses informations en temps réel. Cela concerne, par exemple, l'affichage des nouvelles publications dans les fils, la réception de nouveaux messages, les nouveaux abonnements, etc.
## Se connecter aux flux ## ストリームに接続する
Pour utiliser l'API de streaming, vous devez d'abord effectuer une connexion **websocket** au serveur Misskey. ストリーミングAPIを利用するには、まずMisskeyサーバーに**websocket**接続する必要があります。
Veuillez vous connecter à l'URL suivante avec le nom de paramètre `i` et inclure les informations d'authentification dans la connexion websocket.Par exemple : 以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
``` ```
%WS_URL%/streaming?i=xxxxxxxxxxxxxxx %WS_URL%/streaming?i=xxxxxxxxxxxxxxx
``` ```
Les informations d'identification sont votre clé API ou, en cas de connexion au flux depuis votre application, le jeton d'accès de l'utilisateur. 認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
<div class="ui info"> <div class="ui info">
<p><i class="fas fa-info-circle"></i> Pour obtenir des informations sur l'obtention d'accréditations, veuillez consulter <a href="./api">ce document</a>.</p> <p><i class="fas fa-info-circle"></i> 認証情報の取得については、<a href="./api">こちらのドキュメント</a>をご確認ください。</p>
</div> </div>
--- ---
Vous pouvez omettre les informations d'authentification, mais dans ce cas, vous utiliserez le système sans vous connecter, et les informations que vous pourrez recevoir et les opérations que vous pourrez effectuer seront limitées.Par exemple : 認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
``` ```
%WS_URL%/streaming %WS_URL%/streaming
@ -27,15 +27,15 @@ Vous pouvez omettre les informations d'authentification, mais dans ce cas, vous
--- ---
Une fois que vous êtes connecté au flux, vous pouvez utiliser l'API comme décrit ci-dessous, ou vous abonner aux messages. Cependant, à ce stade, vous ne pouvez pas recevoir de nouveaux messages sur votre fil, par exemple. Pour ce faire, vous devez vous connecter à un **canal** sur le flux, comme décrit ci-dessous. ストリームに接続すると、後述するAPI操作や、投稿の購読を行ったりすることができます。 しかしまだこの段階では、例えばタイムラインへの新しい投稿を受信したりすることはできません。 それを行うには、ストリーム上で、後述する**チャンネル**に接続する必要があります。
**Toutes les interactions dans le flux sont JSON.** **ストリームでのやり取りはすべてJSONです。**
## Canaux ## Canaux
L'API de streaming de Misskey possède le concept de canaux.Il s'agit d'un mécanisme permettant de séparer les informations que vous envoyez et recevez. Si vous vous connectez simplement à un flux Misskey, vous ne pourrez pas encore recevoir les messages de votre timeline en temps réel. En vous connectant aux canaux du flux, vous pourrez recevoir diverses informations et en envoyer. MisskeyのストリーミングAPIにはチャンネルという概念があります。これは、送受信する情報を分離するための仕組みです。 Misskeyのストリームに接続しただけでは、まだリアルタイムでタイムラインの投稿を受信したりはできません。 ストリーム上でチャンネルに接続することで、様々な情報を受け取ったり情報を送信したりすることができるようになります。
### Se connecter à un canal ### チャンネルに接続する
Pour se connecter à un canal, envoyez les données suivantes au flux en JSON : チャンネルに接続するには、次のようなデータをJSONでストリームに送信します:
```json ```json
{ {
@ -50,19 +50,19 @@ Pour se connecter à un canal, envoyez les données suivantes au flux en JSON :
} }
``` ```
Ici, ここで、
* Définissez `channel` au nom du canal auquel vous voulez vous connecter.Les types de canaux sont décrits ci-dessous. * `channel`には接続したいチャンネル名を設定します。チャンネルの種類については後述します。
* `id` est un identifiant arbitraire pour interagir avec ce canal.En effet, le flux contient une variété de messages, et nous devons identifier de quel canal provient le message.Cet ID peut être un UUID ou une sorte de numéro aléatoire. * `id`にはそのチャンネルとやり取りするための任意のIDを設定します。ストリームでは様々なメッセージが流れるので、そのメッセージがどのチャンネルからのものなのか識別する必要があるからです。このIDは、UUIDや、乱数のようなもので構いません。
* `params` sont les paramètres utilisés pour se connecter au canal.Les différents canaux nécessitent des paramètres différents pour la connexion.Lors de la connexion à un canal qui ne nécessite pas de paramètres, cette propriété peut être omise. * `params`はチャンネルに接続する際のパラメータです。チャンネルによって接続時に必要とされるパラメータは異なります。パラメータ不要のチャンネルに接続する際は、このプロパティは省略可能です。
<div class="ui info"> <div class="ui info">
<p><i class="fas fa-info-circle"></i> L'ID est "par connexion de canal", et non par canal. En effet, dans certains cas, plusieurs connexions sont établies sur le même canal avec des paramètres différents.</p> <p><i class="fas fa-info-circle"></i> IDはチャンネルごとではなく「チャンネルの接続ごと」です。なぜなら、同じチャンネルに異なるパラメータで複数接続するケースもあるからです。</p>
</div> </div>
### Recevoir des messages du canal ### チャンネルからのメッセージを受け取る
Par exemple, lorsqu'un événement est émis dans l'un des canaux du fil en raison de la publication d'un nouveau message.En recevant ce message, vous saurez en temps réel qu'une nouvelle publication a été faite sur votre fil. 例えばタイムラインのチャンネルなら、新しい投稿があった時にメッセージを発します。そのメッセージを受け取ることで、タイムラインに新しい投稿がされたことをリアルタイムで知ることができます。
Lorsqu'un canal émet un message, les données suivantes sont diffusées en JSON : チャンネルがメッセージを発すると、次のようなデータがJSONでストリームに流れてきます:
```json ```json
{ {
type: 'channel', type: 'channel',
@ -76,15 +76,15 @@ Lorsqu'un canal émet un message, les données suivantes sont diffusées en JSON
} }
``` ```
Ici, ここで、
* `id` est réglé sur l'ID que vous avez défini lors de la connexion à ce canal comme décrit ci-dessus.Cela vous permettra de savoir de quel canal provient ce message. * `id`には前述したそのチャンネルに接続する際に設定したIDが設定されています。これで、このメッセージがどのチャンネルからのものなのか知ることができます。
* `type` est défini comme le type du message.Le type de message qui sera diffusé dépend du canal. * `type`にはメッセージの種類が設定されます。チャンネルによって、どのような種類のメッセージが流れてくるかは異なります。
* `body` est défini comme le contenu du message.En fonction du canal, le type de message qui sera diffusé dépendra du canal. * `body`にはメッセージの内容が設定されます。チャンネルによって、どのような内容のメッセージが流れてくるかは異なります。
### Envoi d'un message à un canal ### チャンネルに向けてメッセージを送信する
Selon le canal, il se peut que vous ne receviez pas seulement des messages, mais que vous puissiez également envoyer certains messages et effectuer certaines opérations. チャンネルによっては、メッセージを受け取るだけでなく、こちらから何かメッセージを送信し、何らかの操作を行える場合があります。
Pour envoyer un message à un canal, envoyez les données suivantes au flux en JSON : チャンネルにメッセージを送信するには、次のようなデータをJSONでストリームに送信します:
```json ```json
{ {
type: 'channel', type: 'channel',
@ -98,13 +98,13 @@ Pour envoyer un message à un canal, envoyez les données suivantes au flux en J
} }
``` ```
Ici, ここで、
* `id` doit être réglé sur l'ID que vous avez défini lors de la connexion à ce canal comme décrit ci-dessus.Cela vous permettra d'identifier le canal auquel ce message est destiné. * `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。これで、このメッセージがどのチャンネルに向けたものなのか識別させることができます。
* `type` définit le type du message.Les différents canaux acceptent différents types de messages. * `type`にはメッセージの種類を設定します。チャンネルによって、どのような種類のメッセージを受け付けるかは異なります。
* `body` est défini comme le contenu du message.Les différents canaux acceptent différents types de messages. * `body`にはメッセージの内容を設定します。チャンネルによって、どのような内容のメッセージを受け付けるかは異なります。
### Déconnexion d'un canal ### チャンネルから切断する
Pour se déconnecter d'un canal, envoyez les données suivantes au flux en JSON : チャンネルから切断するには、次のようなデータをJSONでストリームに送信します:
```json ```json
{ {
@ -115,14 +115,14 @@ Pour se déconnecter d'un canal, envoyez les données suivantes au flux en JSON
} }
``` ```
Ici, ここで、
* `id` doit être réglé sur l'ID que vous avez défini lors de la connexion à ce canal comme décrit ci-dessus. * `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。
## Faire une requête API via le flux ## ストリームを経由してAPIリクエストする
Si vous effectuez une requête d'API via un flux, vous pouvez utiliser l'API sans générer de requête HTTP.Cela peut rendre votre code plus concis et améliorer les performances. ストリームを経由してAPIリクエストすると、HTTPリクエストを発生させずにAPIを利用できます。そのため、コードを簡潔にできたり、パフォーマンスの向上を見込めるかもしれません。
Pour effectuer une demande d'API via un flux, envoyez les données suivantes au flux en JSON : ストリームを経由してAPIリクエストするには、次のようなデータをJSONでストリームに送信します:
```json ```json
{ {
type: 'api', type: 'api',
@ -136,18 +136,18 @@ Pour effectuer une demande d'API via un flux, envoyez les données suivantes au
} }
``` ```
Ici, ここで、
* `id` doit être défini comme un identifiant unique pour chaque demande d'API afin d'identifier la réponse de l'API.Il peut s'agir de quelque chose comme un UUID ou un simple nombre aléatoire. * `id`には、APIのレスポンスを識別するための、APIリクエストごとの一意なIDを設定する必要があります。UUIDや、簡単な乱数のようなもので構いません。
* `endpoint` est le point de terminaison de l'API que vous voulez demander. * `endpoint`には、あなたがリクエストしたいAPIのエンドポイントを指定します。
* `data` contient les paramètres de la terminaison. * `data`には、エンドポイントのパラメータを含めます。
<div class="ui info"> <div class="ui info">
<p><i class="fas fa-info-circle"></i> Veuillez vous reporter à la référence de l'API pour les points de terminaison et les paramètres de l'API.</p> <p><i class="fas fa-info-circle"></i> APIのエンドポイントやパラメータについてはAPIリファレンスをご確認ください。</p>
</div> </div>
### Réception des réponses ### レスポンスの受信
Lorsque vous faites une demande à l'API, la réponse viendra du flux dans le format suivant. APIへリクエストすると、レスポンスがストリームから次のような形式で流れてきます。
```json ```json
{ {
@ -158,23 +158,23 @@ Lorsque vous faites une demande à l'API, la réponse viendra du flux dans le fo
} }
``` ```
Ici, ここで、
* La partie `xxxxxxxxxxxxxxxx` contient le `id` qui a été défini au moment de la demande.Cela vous permet de déterminer à quelle demande il répond. * `xxxxxxxxxxxxxxxx`の部分には、リクエストの際に設定された`id`が含まれています。これにより、どのリクエストに対するレスポンスなのか判別することができます。
* `body` contient la réponse. * `body`には、レスポンスが含まれています。
## Capture de message ## 投稿のキャプチャ
Misskey propose un mécanisme appelé post-capture.Il s'agit de la possibilité de recevoir un flux d'événements pour un message donné. Misskeyは投稿のキャプチャと呼ばれる仕組みを提供しています。これは、指定した投稿のイベントをストリームで受け取る機能です。
Par exemple, supposons une situation dans laquelle le fil est affichée pour un utilisateur.Supposons maintenant que quelqu'un réagisse à l'un des messages de ce fil. 例えばタイムラインを取得してユーザーに表示したとします。ここで誰かがそのタイムラインに含まれるどれかの投稿に対してリアクションしたとします。
Cependant, comme le client n'a aucun moyen de savoir qu'un message a reçu une réaction, il n'est pas possible de refléter la réaction en temps réel sur le message dans le fil. しかし、クライアントからするとある投稿にリアクションが付いたことなどは知る由がないため、リアルタイムでリアクションをタイムライン上の投稿に反映して表示するといったことができません。
Pour résoudre ce problème, Misskey fournit un mécanisme de post-capture.Lorsque vous capturez un message, vous recevez des événements liés à ce message, ce qui vous permet de refléter les réactions en temps réel. この問題を解決するために、Misskeyは投稿のキャプチャ機構を用意しています。投稿をキャプチャすると、その投稿に関するイベントを受け取ることができるため、リアルタイムでリアクションを反映させたりすることが可能になります。
### Capturer un message ### 投稿をキャプチャする
Pour capturer un message, envoyez un message comme le suivant au flux : 投稿をキャプチャするには、ストリームに次のようなメッセージを送信します:
```json ```json
{ {
@ -185,12 +185,12 @@ Pour capturer un message, envoyez un message comme le suivant au flux :
} }
``` ```
Ici, ここで、
* Définissez `id` comme l'`id` du message que vous voulez capturer. * `id`にキャプチャしたい投稿の`id`を設定します。
Lorsque vous envoyez ce message, vous demandez à Misskey de le saisir, et les événements liés à ce message se succéderont à partir de ce moment-là. このメッセージを送信すると、Misskeyにキャプチャを要請したことになり、以後、その投稿に関するイベントが流れてくるようになります。
Par exemple, lorsqu'un message suscite une réaction, vous verrez apparaître un message du type suivant : 例えば投稿にリアクションが付いたとすると、次のようなメッセージが流れてきます:
```json ```json
{ {
@ -206,20 +206,20 @@ Par exemple, lorsqu'un message suscite une réaction, vous verrez apparaître un
} }
``` ```
Ici, ここで、
* Le `id` dans le `body` est défini comme l'ID du post qui a déclenché l'événement. * `body`内の`id`に、イベントを発生させた投稿のIDが設定されます。
* Le type de l'événement est défini par `type` dans `body`. * `body`内の`type`に、イベントの種類が設定されます。
* L'attribut `body` dans `body` contient les informations sur l'événement. * `body`内の`body`に、イベントの詳細が設定されます。
#### Type d'événements #### イベントの種類
##### `reacted` ##### `reacted`
Cela se produit lorsqu'une réaction est faite à ce message. その投稿にリアクションがされた時に発生します。
* `reaction` est défini comme le type de réaction. * `reaction`に、リアクションの種類が設定されます。
* `userId` sera défini comme l'ID de l'utilisateur qui a fait la réaction. * `userId`に、リアクションを行ったユーザーのIDが設定されます。
Par exemple : :
```json ```json
{ {
type: 'noteUpdated', type: 'noteUpdated',
@ -235,11 +235,11 @@ Par exemple :
``` ```
##### `deleted` ##### `deleted`
Cela se produit lorsque ce message est supprimé. その投稿が削除された時に発生します。
* `deletedAt` est défini comme la date et l'heure de la suppression. * `deletedAt`に、削除日時が設定されます。
Par exemple : :
```json ```json
{ {
type: 'noteUpdated', type: 'noteUpdated',
@ -254,12 +254,12 @@ Par exemple :
``` ```
##### `pollVoted` ##### `pollVoted`
Déclenché lors du vote sur un sondage dans ce message. その投稿に添付されたアンケートに投票された時に発生します。
* `choice` contient l'ID du choix sélectionné. * `choice`に、選択肢IDが設定されます。
* `userId` sera défini comme l'ID de l'utilisateur qui a voté. * `userId`に、投票を行ったユーザーのIDが設定されます。
Par exemple : :
```json ```json
{ {
type: 'noteUpdated', type: 'noteUpdated',
@ -274,11 +274,11 @@ Par exemple :
} }
``` ```
### Annuler la capture de publication ### 投稿のキャプチャを解除する
Quand une publication n'est plus affichée et que vous n'avez plus besoin de recevoir les événements la concernant, vous pouvez demander l'annulation de la capture. その投稿がもう画面に表示されなくなったりして、その投稿に関するイベントをもう受け取る必要がなくなったときは、キャプチャの解除を申請してください。
Envoyez le message suivant : 次のメッセージを送信します:
```json ```json
{ {
@ -289,66 +289,66 @@ Envoyez le message suivant :
} }
``` ```
Ici, ここで、
* Définissez `id` comme le `id` du message que vous voulez annuler. * `id`にキャプチャを解除したい投稿の`id`を設定します。
Une fois que vous aurez envoyé ce message, aucun autre événement lié au message ne sera diffusé. このメッセージを送信すると、以後、その投稿に関するイベントは流れてこないようになります。
# Liste des canaux # チャンネル一覧
## `main` ## `main`
Les informations de base relatives au compte seront transmises ici.Il n'y a pas de paramètres pour ce canal. アカウントに関する基本的な情報が流れてきます。このチャンネルにパラメータはありません。
### Liste des événements envoyés ### 流れてくるイベント一覧
#### `renote` #### `renote`
Cet événement est déclenché lorsque votre message est renoté.Cela ne se produit pas lorsque vous renotez votre propre message. 自分の投稿がRenoteされた時に発生するイベントです。自分自身の投稿をRenoteしたときは発生しません。
#### `mention` #### `mention`
Il s'agit d'un événement qui se produit lorsque quelqu'un fait vous mentionne. 誰かからメンションされたときに発生するイベントです。
#### `readAllNotifications` #### `readAllNotifications`
Cet événement indique que toutes les notifications qui vous ont été adressées ont été lues.Cet événement peut être utilisé pour désactiver des choses comme "l'icône indiquant qu'il y a une notification" et d'autres cas. 自分宛ての通知がすべて既読になったことを表すイベントです。このイベントを利用して、「通知があることを示すアイコン」のようなものをオフにしたりする等のケースが想定されます。
#### `meUpdated` #### `meUpdated`
Cet événement indique que vos informations ont été mises à jour. 自分の情報が更新されたことを表すイベントです。
#### `follow` #### `follow`
Cet événement se produit lorsque vous suivez quelqu'un. 自分が誰かをフォローしたときに発生するイベントです。
#### `unfollow` #### `unfollow`
Cet événement se produit lorsque vous retirez quelqu'un de vos suivis. 自分が誰かのフォローを解除したときに発生するイベントです。
#### `followed` #### `followed`
Cet événement se produit lorsque vous êtes suivi par quelqu'un. 自分が誰かにフォローされたときに発生するイベントです。
## `homeTimeline` ## `homeTimeline`
Vous verrez ce flux d'informations s'afficher sur votre fil personnel.Il n'y a pas de paramètres pour ce canal. ホームタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### Liste des événements envoyés ### 流れてくるイベント一覧
#### `note` #### `note`
Cet événement est déclenché lorsqu'un nouveau message arrive sur sur fil. タイムラインに新しい投稿が流れてきたときに発生するイベントです。
## `localTimeline` ## `localTimeline`
Vous verrez l'information affichée sur votre fil local.Il n'y a pas de paramètres pour ce canal. ローカルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### Liste des événements envoyés ### 流れてくるイベント一覧
#### `note` #### `note`
Cet événement est déclenché lorsqu'un nouveau message apparaît dans le fil local. ローカルタイムラインに新しい投稿が流れてきたときに発生するイベントです。
## `hybridTimeline` ## `hybridTimeline`
Vous verrez l'information affichée sur le fil social.Il n'y a pas de paramètres pour ce canal. ソーシャルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### Liste des événements envoyés ### 流れてくるイベント一覧
#### `note` #### `note`
Cet événement est déclenché lorsqu'un nouveau message apparaît sur votre fil social. ソーシャルタイムラインに新しい投稿が流れてきたときに発生するイベントです。
## `globalTimeline` ## `globalTimeline`
Vous verrez l'information s'afficher sur le fil global.Il n'y a pas de paramètres pour ce canal. グローバルタイムラインの投稿情報が流れてきます。このチャンネルにパラメータはありません。
### Liste des événements envoyés ### 流れてくるイベント一覧
#### `note` #### `note`
Cet événement est déclenché lorsqu'un nouveau message arrive sur le fil global. グローバルタイムラインに新しい投稿が流れてきたときに発生するイベントです。

View File

@ -43,7 +43,7 @@ Le code des thèmes est écrit sous forme d'objets JSON5. Les thèmes comprennen
* `props` ... Définir un style de thème.Voir les explications ci-après. * `props` ... Définir un style de thème.Voir les explications ci-après.
### Définir un style de thème ### Définir un style de thème
C'est dans `props` que vous définirez le style du thème. Les clés deviendront des noms de variables CSS dont le contenu sera spécifié par les valeurs associées. Par ailleurs, les objets présents par défaut dans `props` sont hérités du thème de base. Ainsi, si le thème de `base` est `clair`, ce sera le fichier [_light.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_light.json5) ; et s'il est `sombre`, le fichier [_dark.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_dark.json5). En bref, s'il n'y a, par exemple, pas de clé `panel` définie dans les `props` du thème, alors ce sera la valeur `panel` du thème de base qui sera prise en compte. C'est dans `props` que vous définirez le style de thème. Les propriétés deviendront des variables CSS et les valeurs associées spécifieront le contenu de ces variables. Par ailleurs, les objets présents par défaut dans `props` sont hérités du thème de base. Ainsi, si le thème de `base` est clair `light` ce sera l'objet [_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5) ; et s'il est sombre `dark` ce sera l'objet [_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5). Cela signifie, par exemple, que s'il n'y pas de propriété `panel` définie dans les `props` du thème, alors ce sera la valeur `panel` du thème de base qui sera prise en compte.
#### Syntaxe des valeurs #### Syntaxe des valeurs
* Codes de couleur Hex * Codes de couleur Hex
@ -52,11 +52,11 @@ C'est dans `props` que vous définirez le style du thème. Les clés deviendront
* Ex. : `rgb(0, 255, 0)` * Ex. : `rgb(0, 255, 0)`
* Couleurs avec les valeurs RVBA : `rgba(r, g, b, a)` * Couleurs avec les valeurs RVBA : `rgba(r, g, b, a)`
* Ex. : `rgba(0, 255, 0, 0.5)` * Ex. : `rgba(0, 255, 0, 0.5)`
* Appeler les valeurs d'autres clés * Appeler les valeurs d'autres propriétés
* Entrer `@{keyname}` pour appeler la valeur d'une autre clé. Remplacer alors `{keyname}` par le nom de la clé que vous souhaitez appeler. * Entrer `@{keyname}` pour utiliser la valeur de la propriété citée. Remplacer alors `{keyname}` par le nom de la propriété que vous souhaitez citer.
* Ex. : `@panel` * Ex. : `@panel`
* Constantes (voir ci-dessous) * Constantes (voir ci-dessous)
* Entrer `${constantname}` vous permet d'appeler une constante. Remplacer alors `{constantname}` par le nom de la constante que vous souhaitez appeler. * Entrer `${constantname}` pour utiliser la valeur de la constante citée.Remplacer alors `{constantname}` par la nom de la constante que vous souhaitez citer.
* Ex. : `$main` * Ex. : `$main`
* Fonctions (voir ci-dessous) * Fonctions (voir ci-dessous)
* `:{functionname}<{argument}<{color}` * `:{functionname}<{argument}<{color}`

View File

@ -1,9 +1,9 @@
# Deck # Deck
Il deck è una delle interfacce utente disponibili.Ti consente di configurare varie colonne fianco a fianco, con molte possibilità di personalizzazione, per ottenere uno schermo più ricco in contenuti. Il deck è una delle interfacce utente disponibili.「カラム」と呼ばれるビューを複数並べて表示させることで、カスタマイズ性が高く、情報量の多いUIが構築できることが特徴です。
## Aggiungere colonne ## Aggiungere colonne
Puoi aggiungere una colonna facendo un clic destro nello sfondo del deck, poi selezionando "Aggiungi colonna". Puoi aggiungere una colonna facendo un clic destro nello sfondo del deck, poi scegliendo "Aggiungi colonna".
## Spostare colonne ## Spostare colonne
カラムは、ドラッグアンドドロップで他のカラムと位置を入れ替えることが出来るほか、カラムメニュー(カラムのヘッダー右クリック)から位置を移動させることもできます。 カラムは、ドラッグアンドドロップで他のカラムと位置を入れ替えることが出来るほか、カラムメニュー(カラムのヘッダー右クリック)から位置を移動させることもできます。

View File

@ -43,7 +43,7 @@ Il codice dei temi è scritto a forma di oggetti JSON5. I temi contengono gli og
* `props` ... Imposta uno stile di tema. (Vedi spiegazioni sotto.) * `props` ... Imposta uno stile di tema. (Vedi spiegazioni sotto.)
### Impostare uno stile di tema ### Impostare uno stile di tema
Puoi configurare lo stile del tema dentro le `props`. Le chiavi diventeranno nomi di variabili CSS, il cui contenuto verrà definito dai valori associati ad esse. Inoltre, gli oggetti presenti in `props` per impostazione predefinita vengono ereditati dal tema di base. Il tema di base sarà [_light.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_light.json5) per una `base` `chiara`, e [_dark.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_dark.json5) per una `base` `scura`. Cioè, se non viene definita una chiave `panel` nelle `props` del tema, si terrà conto del valore <0>panel</0> predefinito del tema usato. `props`下にはテーマのスタイルを定義します。 キーがCSSの変数名になり、バリューで中身を指定します。 なお、この`props`オブジェクトはベーステーマから継承されます。 ベーステーマは、このテーマの`base`が`light`なら[_light.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_light.json5)で、`dark`なら[_dark.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_dark.json5)です。 つまり、このテーマ内の`props`に`panel`というキーが無くても、そこにはベーステーマの`panel`があると見なされます。
#### Sintassi dei valori #### Sintassi dei valori
* Colori HEX * Colori HEX
@ -52,17 +52,17 @@ Puoi configurare lo stile del tema dentro le `props`. Le chiavi diventeranno nom
* Es.: `rgb(0, 255, 0)` * Es.: `rgb(0, 255, 0)`
* Colori `RGBA(r, g, b, a)` * Colori `RGBA(r, g, b, a)`
* Es.: `rgba(0, 255, 0, 0.5)` * Es.: `rgba(0, 255, 0, 0.5)`
* Chiamare valori di altre chiavi * 他のキーの値の参照
* Inserisci `@{keyname}` per chiamare il valore di un'altra chiave. Bisogna sostituire il testo `{keyname}` col nome della chiave che vuoi chiamare. * `@{キー名}`と書くと他のキーの値の参照になります。`{キー名}`は参照したいキーの名前に置き換えます。
* Es.: `@panel` * Es.: `@panel`
* Costanti (vedi sotto) * Costanti (vedi sotto)
* Inserisci `${constantname}` per chiamare una costante.Bisogna sostituire il testo `{constantname}` col nome della costante che vuoi chiamare. * `${定数名}`と書くと定数の参照になります。`{定数名}`は参照したい定数の名前に置き換えます。
* Es.: `$main` * Es.: `$main`
* Funzioni (vedi sotto) * Funzioni (vedi sotto)
* `:{functionname}<{argument}<{color}` * `:{functionname}<{argument}<{color}`
#### Costanti #### Costanti
Può essere vantaggioso usare una costante nei casi in cui non vuoi che un valore produca una variabile CSS, perché lo vuoi utilizzare come valore di un'altra variabile CSS. In tal caso, basta aggiungere `$` davanti al nome della chiave affinché non generi variabile CSS. 「CSS変数として出力はしたくないが、他のCSS変数の値として使いまわしたい」値があるときは、定数を使うと便利です。 キー名を`$`で始めると、そのキーはCSS変数として出力されません。
#### Funzioni #### Funzioni
wip wip

View File

@ -1,53 +0,0 @@
import { Entity, Index, Column, PrimaryColumn } from 'typeorm';
import { id } from '../id';
@Entity()
export class Ad {
@PrimaryColumn(id())
public id: string;
@Index()
@Column('timestamp with time zone', {
comment: 'The created date of the Ad.'
})
public createdAt: Date;
@Index()
@Column('timestamp with time zone', {
comment: 'The expired date of the Ad.'
})
public expiresAt: Date;
@Column('varchar', {
length: 32, nullable: false
})
public place: string;
@Column('varchar', {
length: 32, nullable: false
})
public priority: string;
@Column('varchar', {
length: 1024, nullable: false
})
public url: string;
@Column('varchar', {
length: 1024, nullable: false
})
public imageUrl: string;
@Column('varchar', {
length: 8192, nullable: false
})
public memo: string;
constructor(data: Partial<Ad>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View File

@ -1,30 +0,0 @@
import { PrimaryColumn, Entity, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
import { id } from '../id';
import { User } from './user';
@Entity()
export class PasswordResetRequest {
@PrimaryColumn(id())
public id: string;
@Column('timestamp with time zone')
public createdAt: Date;
@Index({ unique: true })
@Column('varchar', {
length: 256,
})
public token: string;
@Index()
@Column({
...id(),
})
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE'
})
@JoinColumn()
public user: User | null;
}

View File

@ -60,8 +60,6 @@ import { MutedNote } from './entities/muted-note';
import { ChannelFollowing } from './entities/channel-following'; import { ChannelFollowing } from './entities/channel-following';
import { ChannelNotePining } from './entities/channel-note-pining'; import { ChannelNotePining } from './entities/channel-note-pining';
import { RegistryItem } from './entities/registry-item'; import { RegistryItem } from './entities/registry-item';
import { Ad } from './entities/ad';
import { PasswordResetRequest } from './entities/password-reset-request';
export const Announcements = getRepository(Announcement); export const Announcements = getRepository(Announcement);
export const AnnouncementReads = getRepository(AnnouncementRead); export const AnnouncementReads = getRepository(AnnouncementRead);
@ -124,5 +122,3 @@ export const Channels = getCustomRepository(ChannelRepository);
export const ChannelFollowings = getRepository(ChannelFollowing); export const ChannelFollowings = getRepository(ChannelFollowing);
export const ChannelNotePinings = getRepository(ChannelNotePining); export const ChannelNotePinings = getRepository(ChannelNotePining);
export const RegistryItems = getRepository(RegistryItem); export const RegistryItems = getRepository(RegistryItem);
export const Ads = getRepository(Ad);
export const PasswordResetRequests = getRepository(PasswordResetRequest);

View File

@ -200,6 +200,8 @@ export class NoteRepository extends Repository<Note> {
mentions: note.mentions.length > 0 ? note.mentions : undefined, mentions: note.mentions.length > 0 ? note.mentions : undefined,
uri: note.uri || undefined, uri: note.uri || undefined,
url: note.url || undefined, url: note.url || undefined,
_featuredId_: (note as any)._featuredId_ || undefined,
_prId_: (note as any)._prId_ || undefined,
...(opts.detail ? { ...(opts.detail ? {
reply: note.replyId ? this.pack(note.reply || note.replyId, me, { reply: note.replyId ? this.pack(note.reply || note.replyId, me, {
@ -446,7 +448,14 @@ export const packedNoteSchema = {
optional: false as const, nullable: true as const, optional: false as const, nullable: true as const,
description: 'The human readable url of a note. it will be null when the note is local.', description: 'The human readable url of a note. it will be null when the note is local.',
}, },
_featuredId_: {
type: 'string' as const,
optional: false as const, nullable: true as const,
},
_prId_: {
type: 'string' as const,
optional: false as const, nullable: true as const,
},
myReaction: { myReaction: {
type: 'object' as const, type: 'object' as const,
optional: true as const, nullable: true as const, optional: true as const, nullable: true as const,

View File

@ -1,45 +0,0 @@
import $ from 'cafy';
import define from '../../../define';
import { Ads } from '../../../../../models';
import { genId } from '@/misc/gen-id';
export const meta = {
tags: ['admin'],
requireCredential: true as const,
requireModerator: true,
params: {
url: {
validator: $.str.min(1)
},
memo: {
validator: $.str
},
place: {
validator: $.str
},
priority: {
validator: $.str
},
expiresAt: {
validator: $.num.int()
},
imageUrl: {
validator: $.str.min(1)
}
},
};
export default define(meta, async (ps) => {
await Ads.insert({
id: genId(),
createdAt: new Date(),
expiresAt: new Date(ps.expiresAt),
url: ps.url,
imageUrl: ps.imageUrl,
priority: ps.priority,
place: ps.place,
memo: ps.memo,
});
});

View File

@ -1,34 +0,0 @@
import $ from 'cafy';
import define from '../../../define';
import { ID } from '@/misc/cafy-id';
import { Ads } from '../../../../../models';
import { ApiError } from '../../../error';
export const meta = {
tags: ['admin'],
requireCredential: true as const,
requireModerator: true,
params: {
id: {
validator: $.type(ID)
}
},
errors: {
noSuchAd: {
message: 'No such ad.',
code: 'NO_SUCH_AD',
id: 'ccac9863-3a03-416e-b899-8a64041118b1'
}
}
};
export default define(meta, async (ps, me) => {
const ad = await Ads.findOne(ps.id);
if (ad == null) throw new ApiError(meta.errors.noSuchAd);
await Ads.delete(ad.id);
});

View File

@ -1,36 +0,0 @@
import $ from 'cafy';
import { ID } from '@/misc/cafy-id';
import define from '../../../define';
import { Ads } from '../../../../../models';
import { makePaginationQuery } from '../../../common/make-pagination-query';
export const meta = {
tags: ['admin'],
requireCredential: true as const,
requireModerator: true,
params: {
limit: {
validator: $.optional.num.range(1, 100),
default: 10
},
sinceId: {
validator: $.optional.type(ID),
},
untilId: {
validator: $.optional.type(ID),
},
},
};
export default define(meta, async (ps) => {
const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId)
.andWhere('ad.expiresAt > :now', { now: new Date() });
const ads = await query.take(ps.limit!).getMany();
return ads;
});

View File

@ -1,59 +0,0 @@
import $ from 'cafy';
import define from '../../../define';
import { ID } from '@/misc/cafy-id';
import { Ads } from '../../../../../models';
import { ApiError } from '../../../error';
export const meta = {
tags: ['admin'],
requireCredential: true as const,
requireModerator: true,
params: {
id: {
validator: $.type(ID)
},
memo: {
validator: $.str
},
url: {
validator: $.str.min(1)
},
imageUrl: {
validator: $.str.min(1)
},
place: {
validator: $.str
},
priority: {
validator: $.str
},
expiresAt: {
validator: $.num.int()
},
},
errors: {
noSuchAd: {
message: 'No such ad.',
code: 'NO_SUCH_AD',
id: 'b7aa1727-1354-47bc-a182-3a9c3973d300'
}
}
};
export default define(meta, async (ps, me) => {
const ad = await Ads.findOne(ps.id);
if (ad == null) throw new ApiError(meta.errors.noSuchAd);
await Ads.update(ad.id, {
url: ps.url,
place: ps.place,
priority: ps.priority,
memo: ps.memo,
imageUrl: ps.imageUrl,
expiresAt: new Date(ps.expiresAt),
});
});

View File

@ -2,9 +2,8 @@ import $ from 'cafy';
import config from '@/config'; import config from '@/config';
import define from '../define'; import define from '../define';
import { fetchMeta } from '@/misc/fetch-meta'; import { fetchMeta } from '@/misc/fetch-meta';
import { Ads, Emojis, Users } from '../../../models'; import { Emojis, Users } from '../../../models';
import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits';
import { MoreThan } from 'typeorm';
export const meta = { export const meta = {
desc: { desc: {
@ -194,30 +193,6 @@ export const meta = {
} }
} }
}, },
ads: {
type: 'array' as const,
optional: false as const, nullable: false as const,
items: {
type: 'object' as const,
optional: false as const, nullable: false as const,
properties: {
place: {
type: 'string' as const,
optional: false as const, nullable: false as const
},
url: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'url'
},
imageUrl: {
type: 'string' as const,
optional: false as const, nullable: false as const,
format: 'url'
},
}
}
},
requireSetup: { requireSetup: {
type: 'boolean' as const, type: 'boolean' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -468,12 +443,6 @@ export default define(meta, async (ps, me) => {
} }
}); });
const ads = await Ads.find({
where: {
expiresAt: MoreThan(new Date())
},
});
const response: any = { const response: any = {
maintainerName: instance.maintainerName, maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail, maintainerEmail: instance.maintainerEmail,
@ -508,12 +477,6 @@ export default define(meta, async (ps, me) => {
logoImageUrl: instance.logoImageUrl, logoImageUrl: instance.logoImageUrl,
maxNoteTextLength: Math.min(instance.maxNoteTextLength, DB_MAX_NOTE_TEXT_LENGTH), maxNoteTextLength: Math.min(instance.maxNoteTextLength, DB_MAX_NOTE_TEXT_LENGTH),
emojis: await Emojis.packMany(emojis), emojis: await Emojis.packMany(emojis),
ads: ads.map(ad => ({
url: ad.url,
place: ad.place,
priority: ad.priority,
imageUrl: ad.imageUrl,
})),
enableEmail: instance.enableEmail, enableEmail: instance.enableEmail,
enableTwitterIntegration: instance.enableTwitterIntegration, enableTwitterIntegration: instance.enableTwitterIntegration,

View File

@ -1,73 +0,0 @@
import $ from 'cafy';
import { publishMainStream } from '../../../services/stream';
import define from '../define';
import rndstr from 'rndstr';
import config from '@/config';
import * as ms from 'ms';
import { Users, UserProfiles, PasswordResetRequests } from '../../../models';
import { sendEmail } from '../../../services/send-email';
import { ApiError } from '../error';
import { genId } from '@/misc/gen-id';
import { IsNull } from 'typeorm';
export const meta = {
requireCredential: false as const,
limit: {
duration: ms('1hour'),
max: 3
},
params: {
username: {
validator: $.str
},
email: {
validator: $.str
},
},
errors: {
}
};
export default define(meta, async (ps) => {
const user = await Users.findOne({
usernameLower: ps.username.toLowerCase(),
host: IsNull()
});
// 合致するユーザーが登録されていなかったら無視
if (user == null) {
return;
}
const profile = await UserProfiles.findOneOrFail(user.id);
// 合致するメアドが登録されていなかったら無視
if (profile.email !== ps.email) {
return;
}
// メアドが認証されていなかったら無視
if (!profile.emailVerified) {
return;
}
const token = rndstr('a-z0-9', 64);
await PasswordResetRequests.insert({
id: genId(),
createdAt: new Date(),
userId: profile.userId,
token
});
const link = `${config.url}/reset-password/${token}`;
sendEmail(ps.email, 'Password reset requested',
`To reset password, please click this link:<br><a href="${link}">${link}</a>`,
`To reset password, please click this link: ${link}`);
});

View File

@ -1,45 +0,0 @@
import $ from 'cafy';
import * as bcrypt from 'bcryptjs';
import { publishMainStream } from '../../../services/stream';
import define from '../define';
import { Users, UserProfiles, PasswordResetRequests } from '../../../models';
import { ApiError } from '../error';
export const meta = {
requireCredential: false as const,
params: {
token: {
validator: $.str
},
password: {
validator: $.str
}
},
errors: {
}
};
export default define(meta, async (ps, user) => {
const req = await PasswordResetRequests.findOneOrFail({
token: ps.token,
});
// 発行してから30分以上経過していたら無効
if (Date.now() - req.createdAt.getTime() > 1000 * 60 * 30) {
throw new Error(); // TODO
}
// Generate hash of password
const salt = await bcrypt.genSalt(8);
const hash = await bcrypt.hash(ps.password, salt);
await UserProfiles.update(req.userId, {
password: hash
});
PasswordResetRequests.delete(req.id);
});

View File

@ -20,7 +20,6 @@ import { packedAntennaSchema } from '../../../models/repositories/antenna';
import { packedClipSchema } from '../../../models/repositories/clip'; import { packedClipSchema } from '../../../models/repositories/clip';
import { packedFederationInstanceSchema } from '../../../models/repositories/federation-instance'; import { packedFederationInstanceSchema } from '../../../models/repositories/federation-instance';
import { packedQueueCountSchema } from '../../../models/repositories/queue'; import { packedQueueCountSchema } from '../../../models/repositories/queue';
import { packedGalleryPostSchema } from '@/models/repositories/gallery-post';
export function convertSchemaToOpenApiSchema(schema: Schema) { export function convertSchemaToOpenApiSchema(schema: Schema) {
const res: any = schema; const res: any = schema;
@ -93,5 +92,4 @@ export const schemas = {
Antenna: convertSchemaToOpenApiSchema(packedAntennaSchema), Antenna: convertSchemaToOpenApiSchema(packedAntennaSchema),
Clip: convertSchemaToOpenApiSchema(packedClipSchema), Clip: convertSchemaToOpenApiSchema(packedClipSchema),
FederationInstance: convertSchemaToOpenApiSchema(packedFederationInstanceSchema), FederationInstance: convertSchemaToOpenApiSchema(packedFederationInstanceSchema),
GalleryPost: convertSchemaToOpenApiSchema(packedGalleryPostSchema),
}; };

View File

@ -61,11 +61,6 @@ router.get('/.well-known/nodeinfo', async ctx => {
ctx.body = { links }; ctx.body = { links };
}); });
/* TODO
router.get('/.well-known/change-password', async ctx => {
});
*/
router.get(webFingerPath, async ctx => { router.get(webFingerPath, async ctx => {
const fromId = (id: User['id']): Record<string, any> => ({ const fromId = (id: User['id']): Record<string, any> => ({
id, id,

View File

@ -12,8 +12,7 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import * as childProcess from 'child_process'; import * as childProcess from 'child_process';
import { async, launchServer, signup, post, request, simpleGet, port } from './utils'; import { async, launchServer, signup, post, request, simpleGet } from './utils';
import * as openapi from '@redocly/openapi-core';
// Request Accept // Request Accept
const ONLY_AP = 'application/activity+json'; const ONLY_AP = 'application/activity+json';
@ -75,20 +74,6 @@ describe('Fetch resource', () => {
assert.strictEqual(res.type, JSON); assert.strictEqual(res.type, JSON);
})); }));
it('Validate api.json', async(async () => {
const config = await openapi.loadConfig();
const result = await openapi.bundle({
config,
ref: `http://localhost:${port}/api.json`
});
for (const problem of result.problems) {
console.log(`${problem.message} - ${problem.location[0]?.pointer}`);
}
assert.strictEqual(result.problems.length, 0);
}));
it('GET favicon.ico', async(async () => { it('GET favicon.ico', async(async () => {
const res = await simpleGet('/favicon.ico'); const res = await simpleGet('/favicon.ico');
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);

View File

@ -6,7 +6,7 @@ import * as childProcess from 'child_process';
import * as http from 'http'; import * as http from 'http';
import loadConfig from '../src/config/load'; import loadConfig from '../src/config/load';
export const port = loadConfig().port; const port = loadConfig().port;
export const async = (fn: Function) => (done: Function) => { export const async = (fn: Function) => (done: Function) => {
fn().then(() => { fn().then(() => {

View File

@ -287,31 +287,6 @@
"@nodelib/fs.scandir" "2.1.3" "@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0" fastq "^1.6.0"
"@redocly/ajv@^6.12.3":
version "6.12.4"
resolved "https://registry.yarnpkg.com/@redocly/ajv/-/ajv-6.12.4.tgz#b131c9c11d1bdaa5564f69bcaefe10a4c9a5aa65"
integrity sha512-RB6vWO78v6c+SW/3bZh+XZMr4nGdJKAiPGsBALuUZnLuCiQ7aXCT1AuFHqnfS2gyXbEUEj+kw8p4ux8KdAfs3A==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
"@redocly/openapi-core@1.0.0-beta.44":
version "1.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.0.0-beta.44.tgz#e9a6c50ee2bb18e93b3439ed92c53e440de3aa7a"
integrity sha512-9HNnh1MzvMsLK1liuidFBqWiAsZ2Yg3RY58fcEsy0QruSMdDbn7SoeI1qnXe6O+BkBS+vAP4oVzZDMHCMKGsOQ==
dependencies:
"@redocly/ajv" "^6.12.3"
"@types/node" "^14.11.8"
colorette "^1.2.0"
js-levenshtein "^1.1.6"
js-yaml "^3.14.0"
lodash.isequal "^4.5.0"
minimatch "^3.0.4"
node-fetch "^2.6.1"
yaml-ast-parser "0.0.43"
"@sentry/browser@5.29.2": "@sentry/browser@5.29.2":
version "5.29.2" version "5.29.2"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.29.2.tgz#51adb4005511b1a4a70e4571880fc6653d651f91" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.29.2.tgz#51adb4005511b1a4a70e4571880fc6653d651f91"
@ -874,11 +849,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615"
integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==
"@types/node@^14.11.8":
version "14.14.44"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.44.tgz#df7503e6002847b834371c004b372529f3f85215"
integrity sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==
"@types/nodemailer@6.4.1": "@types/nodemailer@6.4.1":
version "6.4.1" version "6.4.1"
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.1.tgz#31f96f4410632f781d3613bd1f4293649e423f75" resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.1.tgz#31f96f4410632f781d3613bd1f4293649e423f75"
@ -2820,7 +2790,7 @@ color@^3.1.1, color@^3.1.3:
color-convert "^1.9.1" color-convert "^1.9.1"
color-string "^1.5.4" color-string "^1.5.4"
colorette@^1.2.0, colorette@^1.2.1, colorette@^1.2.2: colorette@^1.2.1, colorette@^1.2.2:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
@ -5787,11 +5757,6 @@ js-beautify@^1.6.12:
mkdirp "~1.0.3" mkdirp "~1.0.3"
nopt "^4.0.3" nopt "^4.0.3"
js-levenshtein@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
js-sha3@0.8.0: js-sha3@0.8.0:
version "0.8.0" version "0.8.0"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
@ -5829,14 +5794,6 @@ js-yaml@^3.13.1:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^4.0.0" esprima "^4.0.0"
js-yaml@^3.14.0:
version "3.14.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@~3.7.0: js-yaml@~3.7.0:
version "3.7.0" version "3.7.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
@ -6403,11 +6360,6 @@ lodash.foreach@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.isfinite@^3.3.2: lodash.isfinite@^3.3.2:
version "3.3.2" version "3.3.2"
resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3" resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3"
@ -7014,7 +6966,7 @@ node-addon-api@^3.1.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239"
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
node-fetch@2.6.1, node-fetch@^2.6.1: node-fetch@2.6.1:
version "2.6.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
@ -11429,11 +11381,6 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml-ast-parser@0.0.43:
version "0.0.43"
resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb"
integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==
yaml@^1.10.0: yaml@^1.10.0:
version "1.10.0" version "1.10.0"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
@ -11453,6 +11400,14 @@ yargs-parser@20.2.4, yargs-parser@^20.2.2:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
yargs-parser@5.0.0-security.0, yargs-parser@^5.0.0:
version "5.0.0-security.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz#4ff7271d25f90ac15643b86076a2ab499ec9ee24"
integrity sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==
dependencies:
camelcase "^3.0.0"
object.assign "^4.1.0"
yargs-parser@>=5.0.0-security.0: yargs-parser@>=5.0.0-security.0:
version "20.2.7" version "20.2.7"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
@ -11466,14 +11421,6 @@ yargs-parser@^13.1.2:
camelcase "^5.0.0" camelcase "^5.0.0"
decamelize "^1.2.0" decamelize "^1.2.0"
yargs-parser@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.1.tgz#7ede329c1d8cdbbe209bd25cdb990e9b1ebbb394"
integrity sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==
dependencies:
camelcase "^3.0.0"
object.assign "^4.1.0"
yargs-unparser@2.0.0: yargs-unparser@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"