diff --git a/entrypoint.example.ts b/entrypoint.example.ts index 61fe279..57c3087 100644 --- a/entrypoint.example.ts +++ b/entrypoint.example.ts @@ -9,6 +9,7 @@ import { pubkeyBanPolicy, rateLimitPolicy, readStdin, + regexPolicy, writeStdout, } from './mod.ts'; @@ -20,6 +21,7 @@ for await (const msg of readStdin()) { [rateLimitPolicy, { whitelist: ['127.0.0.1'] }], [pubkeyBanPolicy, ['e810fafa1e89cdf80cced8e013938e87e21b699b24c8570537be92aec4b12c18']], [keywordPolicy, ['https://t.me/']], + [regexPolicy, /(🟠|🔥|😳)ChtaGPT/i], ]); writeStdout(result); diff --git a/mod.ts b/mod.ts index b5f080f..cf5f336 100644 --- a/mod.ts +++ b/mod.ts @@ -5,6 +5,7 @@ export { default as noopPolicy } from './src/policies/noop-policy.ts'; export { default as pubkeyBanPolicy } from './src/policies/pubkey-ban-policy.ts'; export { default as rateLimitPolicy } from './src/policies/rate-limit-policy.ts'; export { default as readOnlyPolicy } from './src/policies/read-only-policy.ts'; +export { default as regexPolicy } from './src/policies/regex-policy.ts'; export { default as whitelistPolicy } from './src/policies/whitelist-policy.ts'; export { readStdin, writeStdout } from './src/io.ts'; diff --git a/src/policies/hellthread-policy.test.ts b/src/policies/hellthread-policy.test.ts index b350e78..7b440f1 100644 --- a/src/policies/hellthread-policy.test.ts +++ b/src/policies/hellthread-policy.test.ts @@ -1,4 +1,4 @@ -import { assert } from '../deps.ts'; +import { assertEquals } from '../deps.ts'; import { buildEvent, buildInputMessage } from '../test.ts'; import hellthreadPolicy from './hellthread-policy.ts'; @@ -9,6 +9,6 @@ Deno.test('blocks events with too many mentioned users', async () => { const msg0 = buildInputMessage(); const msg1 = buildInputMessage({ event: buildEvent({ tags }) }); - assert((await hellthreadPolicy(msg0, { limit: 1 })).action === 'accept'); - assert((await hellthreadPolicy(msg1, { limit: 1 })).action === 'reject'); + assertEquals((await hellthreadPolicy(msg0, { limit: 1 })).action, 'accept'); + assertEquals((await hellthreadPolicy(msg1, { limit: 1 })).action, 'reject'); }); diff --git a/src/policies/keyword-policy.test.ts b/src/policies/keyword-policy.test.ts index f33f7df..c134d02 100644 --- a/src/policies/keyword-policy.test.ts +++ b/src/policies/keyword-policy.test.ts @@ -1,4 +1,4 @@ -import { assert } from '../deps.ts'; +import { assertEquals } from '../deps.ts'; import { buildEvent, buildInputMessage } from '../test.ts'; import keywordPolicy from './keyword-policy.ts'; @@ -9,7 +9,7 @@ Deno.test('blocks banned pubkeys', async () => { const msg0 = buildInputMessage(); const msg1 = buildInputMessage({ event: buildEvent({ content: '🔥🔥🔥 https://t.me/spam 我想死' }) }); - assert((await keywordPolicy(msg0, words)).action === 'accept'); - assert((await keywordPolicy(msg1, words)).action === 'reject'); - assert((await keywordPolicy(msg1, [])).action === 'accept'); + assertEquals((await keywordPolicy(msg0, words)).action, 'accept'); + assertEquals((await keywordPolicy(msg1, words)).action, 'reject'); + assertEquals((await keywordPolicy(msg1, [])).action, 'accept'); }); diff --git a/src/policies/noop-policy.test.ts b/src/policies/noop-policy.test.ts index 577ee51..a22456a 100644 --- a/src/policies/noop-policy.test.ts +++ b/src/policies/noop-policy.test.ts @@ -1,9 +1,9 @@ -import { assert } from '../deps.ts'; +import { assertEquals } from '../deps.ts'; import { buildInputMessage } from '../test.ts'; import noopPolicy from './noop-policy.ts'; Deno.test('allows events', async () => { const msg = buildInputMessage(); - assert((await noopPolicy(msg)).action === 'accept'); + assertEquals((await noopPolicy(msg)).action, 'accept'); }); diff --git a/src/policies/pubkey-ban-policy.test.ts b/src/policies/pubkey-ban-policy.test.ts index 6cd04c8..d86a14e 100644 --- a/src/policies/pubkey-ban-policy.test.ts +++ b/src/policies/pubkey-ban-policy.test.ts @@ -1,4 +1,4 @@ -import { assert } from '../deps.ts'; +import { assertEquals } from '../deps.ts'; import { buildEvent, buildInputMessage } from '../test.ts'; import pubkeyBanPolicy from './pubkey-ban-policy.ts'; @@ -8,8 +8,8 @@ Deno.test('blocks banned pubkeys', async () => { const msgB = buildInputMessage({ event: buildEvent({ pubkey: 'B' }) }); const msgC = buildInputMessage({ event: buildEvent({ pubkey: 'C' }) }); - assert((await pubkeyBanPolicy(msgA, [])).action === 'accept'); - assert((await pubkeyBanPolicy(msgA, ['A'])).action === 'reject'); - assert((await pubkeyBanPolicy(msgC, ['B', 'A'])).action === 'accept'); - assert((await pubkeyBanPolicy(msgB, ['B', 'A'])).action === 'reject'); + assertEquals((await pubkeyBanPolicy(msgA, [])).action, 'accept'); + assertEquals((await pubkeyBanPolicy(msgA, ['A'])).action, 'reject'); + assertEquals((await pubkeyBanPolicy(msgC, ['B', 'A'])).action, 'accept'); + assertEquals((await pubkeyBanPolicy(msgB, ['B', 'A'])).action, 'reject'); }); diff --git a/src/policies/read-only-policy.test.ts b/src/policies/read-only-policy.test.ts index 3da9cc0..93aeadb 100644 --- a/src/policies/read-only-policy.test.ts +++ b/src/policies/read-only-policy.test.ts @@ -1,4 +1,4 @@ -import { assert } from '../deps.ts'; +import { assertEquals } from '../deps.ts'; import { buildInputMessage } from '../test.ts'; import readOnlyPolicy from './read-only-policy.ts'; @@ -6,5 +6,5 @@ import readOnlyPolicy from './read-only-policy.ts'; Deno.test('always rejects', async () => { const msg = buildInputMessage(); const result = await readOnlyPolicy(msg); - assert(result.action === 'reject'); + assertEquals(result.action, 'reject'); }); diff --git a/src/policies/regex-policy.test.ts b/src/policies/regex-policy.test.ts new file mode 100644 index 0000000..400a984 --- /dev/null +++ b/src/policies/regex-policy.test.ts @@ -0,0 +1,15 @@ +import { assertEquals } from '../deps.ts'; +import { buildEvent, buildInputMessage } from '../test.ts'; + +import regexPolicy from './regex-policy.ts'; + +Deno.test('blocks banned regular expressions', async () => { + const msg = buildInputMessage({ event: buildEvent({ content: '🔥🔥🔥 https://t.me/spam 我想死' }) }); + + assertEquals((await regexPolicy(msg)).action, 'accept'); + assertEquals((await regexPolicy(msg, /https:\/\/t\.me\/\w+/i)).action, 'reject'); + assertEquals((await regexPolicy(msg, /🔥{1,3}/)).action, 'reject'); + assertEquals((await regexPolicy(msg, /🔥{4}/)).action, 'accept'); + assertEquals((await regexPolicy(msg, /🔥$/)).action, 'accept'); + assertEquals((await regexPolicy(msg, /^🔥/)).action, 'reject'); +}); diff --git a/src/policies/regex-policy.ts b/src/policies/regex-policy.ts new file mode 100644 index 0000000..e51da4e --- /dev/null +++ b/src/policies/regex-policy.ts @@ -0,0 +1,22 @@ +import { Policy } from '../types.ts'; + +/** Reject events whose content matches the regex. */ +const regexPolicy: Policy = ({ event: { id, content } }, regex) => { + const isMatch = regex ? regex.test(content) : false; + + if (isMatch) { + return { + id, + action: 'reject', + msg: 'Event matches a banned expression.', + }; + } + + return { + id, + action: 'accept', + msg: '', + }; +}; + +export default regexPolicy; diff --git a/src/policies/whitelist-policy.test.ts b/src/policies/whitelist-policy.test.ts index e6afe32..881fdef 100644 --- a/src/policies/whitelist-policy.test.ts +++ b/src/policies/whitelist-policy.test.ts @@ -1,4 +1,4 @@ -import { assert } from '../deps.ts'; +import { assertEquals } from '../deps.ts'; import { buildEvent, buildInputMessage } from '../test.ts'; import whitelistPolicy from './whitelist-policy.ts'; @@ -8,8 +8,8 @@ Deno.test('allows only whitelisted pubkeys', async () => { const msgB = buildInputMessage({ event: buildEvent({ pubkey: 'B' }) }); const msgC = buildInputMessage({ event: buildEvent({ pubkey: 'C' }) }); - assert((await whitelistPolicy(msgA, [])).action === 'reject'); - assert((await whitelistPolicy(msgA, ['A'])).action === 'accept'); - assert((await whitelistPolicy(msgC, ['B', 'A'])).action === 'reject'); - assert((await whitelistPolicy(msgB, ['B', 'A'])).action === 'accept'); + assertEquals((await whitelistPolicy(msgA, [])).action, 'reject'); + assertEquals((await whitelistPolicy(msgA, ['A'])).action, 'accept'); + assertEquals((await whitelistPolicy(msgC, ['B', 'A'])).action, 'reject'); + assertEquals((await whitelistPolicy(msgB, ['B', 'A'])).action, 'accept'); });