Compare commits

...

5 Commits
stage ... nya

Author SHA1 Message Date
Acid Chicken (硫酸鶏) 936df45e06
Merge branch 'develop' into nya 2019-02-08 01:12:17 +09:00
Acid Chicken (硫酸鶏) e61c84056c
Add syntax recheck guard 2019-01-25 22:18:01 +09:00
Acid Chicken (硫酸鶏) 8c7e56207b
Update note.ts 2019-01-25 13:37:40 +09:00
Acid Chicken (硫酸鶏) dba2833fe9
Fix bug 2019-01-24 20:00:23 +09:00
Acid Chicken (硫酸鶏) 2bac1a90f0
Add nyaize syntax 2019-01-24 19:45:33 +09:00
3 changed files with 120 additions and 17 deletions

View File

@ -370,15 +370,57 @@ export const pack = async (
}
//#endregion
if (_note.user.isCat && _note.text) {
_note.text = (_note.text
// ja-JP
.replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ')
// ko-KR
.replace(/[나-낳]/g, (match: string) => String.fromCharCode(
match.codePointAt(0) + '냐'.charCodeAt(0) - '나'.charCodeAt(0)
))
);
if ((() => { // Recheck syntax
const match = _note.text && _note.text.match(/<\/?!?nya>/ig) || [];
const stack: string[] = [];
for (const tag of [...match]
.map(x => x.toLocaleLowerCase()))
if (tag.includes('/')) {
if (stack.pop() !== tag.replace('/', ''))
return false;
} else
stack.push(tag);
return !stack.length;
})()) {
const nyamap: { [x: string]: string } = {
//#region nyaize: ja-JP
'な': 'にゃ',
'ナ': 'ニャ',
'ナ': 'ニャ'
//#endregion
};
//#region nyaize: ko-KR
const diffKoKr = '냐'.charCodeAt(0) - '나'.charCodeAt(0);
for (let i = '나'.charCodeAt(0); i < '내'.charCodeAt(0); i++)
nyamap[String.fromCharCode(i)] = String.fromCharCode(i + diffKoKr);
//#endregion
const raw: string = _note.text;
const stack = [!!_note.user.isCat];
if (raw) {
_note.text = '';
for (let i = 0; i < raw.length; i++) {
const head = raw[i];
if (head === '<') {
const [, tag, state] = raw.slice(i).match(/^<((\/?!?)nya>)/i) || [, , , ];
if (typeof state === 'string') {
if (state[0] === '/')
stack.shift();
else
stack.unshift(!state);
i += tag.length;
continue;
}
}
_note.text += stack[0] && nyamap[head] || head;
}
}
}
if (!opts.skipHide) {

View File

@ -162,6 +162,18 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
if (data.text) {
data.text = data.text.trim();
const match = data.text.match(/<\/?!?nya>/ig) || [];
const stack: string[] = [];
for (const tag of [...match]
.map(x => x.toLocaleLowerCase()))
if (tag.includes('/')) {
if (stack.pop() !== tag.replace('/', ''))
return rej('Invalid nyaize syntax');
} else
stack.push(tag);
if (stack.length)
return rej('Invalid nyaize syntax');
}
let tags = data.apHashtags;
@ -170,10 +182,11 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
// Parse MFM if needed
if (!tags || !emojis || !mentionedUsers) {
const tokens = data.text ? parse(data.text) : [];
const text = data.text && data.text.replace(/^<\/?!?nya>/ig, '');
const tokens = text ? parse(text) : [];
const cwTokens = data.cw ? parse(data.cw) : [];
const choiceTokens = data.poll && data.poll.choices
? concat((data.poll.choices as IChoice[]).map(choice => parse(choice.text)))
? concat((data.poll.choices as IChoice[]).map(choice => parse(choice.text && choice.text.replace(/^<\/?!?nya>/ig, ''))))
: [];
const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens);
@ -318,7 +331,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
// If it is renote
if (data.renote) {
const type = data.text ? 'quote' : 'renote';
const type = data.text && data.text.replace(/^<\/?!?nya>/ig, '') ? 'quote' : 'renote';
// Notify
if (isLocalUser(data.renote._user)) {
@ -354,7 +367,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
async function renderNoteOrRenoteActivity(data: Option, note: INote) {
if (data.localOnly) return null;
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length == 0)
const content = data.renote && (!data.text || !data.text.replace(/^<\/?!?nya>/ig, '')) && !data.poll && (!data.files || !data.files.length)
? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote._id}`, note)
: renderCreate(await renderNote(note, false), note);
@ -500,15 +513,17 @@ async function insertNote(user: IUser, data: Option, tags: string[], emojis: str
}
function index(note: INote) {
if (note.text == null || config.elasticsearch == null) return;
if (!note.text || !config.elasticsearch) return;
const text = note.text.replace(/^<\/?!?nya>/ig, '');
if (!text) return;
es.index({
index: 'misskey',
type: 'note',
id: note._id.toString(),
body: {
text: note.text
}
body: { text }
});
}

View File

@ -368,6 +368,52 @@ describe('API', () => {
expect(res).have.status(400);
}));
it('can make nyaize enable', async(async () => {
const me = await signup();
const post = {
text: 'なん<nya>なん<!nya>なん</!nya>なん</nya>なん'
};
const res = await request('/notes/create', post, me);
expect(res).have.status(200);
expect(res.body).be.a('object');
expect(res.body).have.property('createdNote');
expect(res.body.createdNote).have.property('text').eql('なんにゃんなんにゃんなん');
}));
it('can make nyaize disable', async(async () => {
const me = await signup();
await request('/i/update', {
isCat: true
}, me);
const post = {
text: 'なん<!nya>なん<nya>なん</nya>なん</!nya>なん'
};
const res = await request('/notes/create', post, me);
expect(res).have.status(200);
expect(res.body).be.a('object');
expect(res.body).have.property('createdNote');
expect(res.body.createdNote).have.property('text').eql('にゃんなんにゃんなんにゃん');
}));
it('throw error when invalid syntax', async(async () => {
const me = await signup();
const post = {
text: 'なん<nya>なん'
};
const res = await request('/notes/create', post, me);
expect(res).have.status(400);
}));
it('存在しないリプライ先で怒られる', async(async () => {
const me = await signup();
const post = {