├── .github
├── FUNDING.yml
├── auto_assign.yml
├── labeler.yml
└── workflows
│ └── label.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── docs
└── experiment-rollout.md
├── experiment-rollout.js
├── experiment-rollout
├── data.json
└── index.js
├── experiments-api.js
└── timestamp.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [Tolga1452]
--------------------------------------------------------------------------------
/.github/auto_assign.yml:
--------------------------------------------------------------------------------
1 | addReviewers: true
2 |
3 | addAssignees: true
4 |
5 | reviewers:
6 | - Lordcobcob
7 | - Pukimaa
8 | - SamomenX
9 | - sndctd
10 | - tayron1
11 | - Tolga1452
12 |
13 | numberOfReviewers: 1
--------------------------------------------------------------------------------
/.github/labeler.yml:
--------------------------------------------------------------------------------
1 | 'scripts:experiments-api':
2 | - experiments-api.js
3 |
4 | 'scripts:experiment-rollout':
5 | - experiment-rollout.js
6 |
--------------------------------------------------------------------------------
/.github/workflows/label.yml:
--------------------------------------------------------------------------------
1 | # This workflow will triage pull requests and apply a label based on the
2 | # paths that are modified in the pull request.
3 | #
4 | # To use this workflow, you will need to set up a .github/labeler.yml
5 | # file with configuration. For more information, see:
6 | # https://github.com/actions/labeler
7 |
8 | name: Labeler
9 | on: [pull_request_target]
10 |
11 | jobs:
12 | label:
13 |
14 | runs-on: ubuntu-latest
15 | permissions:
16 | contents: read
17 | pull-requests: write
18 |
19 | steps:
20 | - uses: actions/labeler@v4
21 | with:
22 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package*.json
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | JUST FOLLOW THE `./docs` FOLDER
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | 
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Assyst Tags
2 |
3 | Advanced tag scripts for [Assyst](https://jacher.io/assyst) bot.
4 |
5 | # Tags
6 |
7 | - [Experiments API](#experiments-api)
8 | - [Experiment Rollout](#experiment-rollout)
9 | - [Timestamp](#timestamp)
10 |
11 | ## Experiments API
12 |
13 | By @Tolga1452
14 |
15 | ### Setup
16 |
17 | Replace the `tag_name` with the name of the tag you want to create here and send the message:
18 |
19 | ```
20 | -t create tag_name {js:
21 | {download:https://raw.githubusercontent.com/discordexperimenthub/assyst-tags/main/experiments-api.js}
22 | experimentsAPI('tag_name');
23 | }
24 | ```
25 |
26 | ### Credits
27 |
28 | - **[API](https://experiments.dscrd.workers.dev) Creator:** @sndctd
29 | - **External Contributor:** @WilsontheWolf
30 |
31 | ## Experiment Rollout
32 |
33 | By @Tolga1452
34 |
35 | - [**Documentation**](https://github.com/discordexperimenthub/assyst-tags/docs/experiment-rollout.md)
36 |
37 | ### Setup
38 |
39 | Replace the `tag_name` with the name of the tag you want to create here and send the message:
40 |
41 | ```
42 | -t create tag_name {js:
43 | {download:https://raw.githubusercontent.com/discordexperimenthub/assyst-tags/main/experiment-rollout/index.js}
44 | experimentRollout('tag_name');
45 | }
46 | ```
47 |
48 | ## Timestamp
49 |
50 | By @Tolga1452
51 |
52 | ### Setup
53 |
54 | Replace the `tag_name` with the name of the tag you want to create here and send the message:
55 |
56 | ```
57 | -t create tag_name {js:
58 | {download:https://raw.githubusercontent.com/discordexperimenthub/assyst-tags/main/timestamp.js}
59 | timestamp('tag_name');
60 | }
61 | ```
62 |
--------------------------------------------------------------------------------
/docs/experiment-rollout.md:
--------------------------------------------------------------------------------
1 | # [`experiment-rollout.js`](https://github.com/discordexperimenthub/assyst-tags#experiment-rollout)
2 |
3 | ## Last Update Timestamp
4 |
5 | The `lastUpdate` value of the `experiment-rollout/index.js` file have to be updated **EVERY TIME** at least one feature data is updated. The value should be UNIX timestamp in milliseconds.
6 |
7 | ## Feature Structure
8 |
9 | > **Note:**
10 | >
11 | > - [Rollout Type](#rollout-types) `-2` only requires `rolloutType` (`-2`) and `replacedBy` fields.
12 | > - [Rollout Type](#rollout-types) `2` only requires `rolloutType` (`2`) and `timestamp` fields.
13 |
14 | | Field | Type | Description | Required |
15 | | --- | --- | --- | --- |
16 | | `experimentType` | number | [Experiment Type](#experiment-types) of the Feature. | ✅ |
17 | | `rolloutType` | number | [Rollout Type](#rollout-types) of the Feature. | ✅ |
18 | | `rate` | number | Total rate of the Feature | ✅ |
19 | | `ranges` | Array\\> | Ranges of the Feature. Can only be optional when rate is `0` or `100`. | ❌ |
20 | | `requirements` | Array\<[Requirement](#requirement-structure)\> | [Requirements](#requirement-structure) of the Feature. | ❌ |
21 | | `priority` | Array<[Priority](#priority-structure)> | [Priority](#priority-structure) data of the rollout of the Feature. | ❌ |
22 | | `timestamp` | number | Timestamp of the rollout of the Feature. Only for [Rollout Type](#rollout-types) `2`. | ❌ |
23 | | `replacedBy` | string | Id of the Feature that replaced this Feature. Only for [Rollout Type](#rollout-types) `-2`. | ❌ |
24 | | `details` | Array<[Detail](#detail-structure)> | [Detailed](#detail-structure) rollout status of the Feature. | ❌ |
25 |
26 | ### Example Feature
27 |
28 | ```json
29 | "channel_summaries": {
30 | "experimentType": 0,
31 | "rolloutType": 3
32 | }
33 | ```
34 |
35 | ## Requirement Structure
36 |
37 | | Field | Type | Description | Required |
38 | | --- | --- | --- | --- |
39 | | `type` | number | [Type](#requirement-types) of the Requirement. | ✅ |
40 | | `value` | Array\ \| number \| Array\ | Value of the Requirement. | ✅ |
41 | | `rate` | number | Rate of the Requirement. | ✅ |
42 | | `ranges` | Array\\> | Ranges of the Requirement. Values of sub-arrays can only be `null` when range is unknown. | ✅ |
43 |
44 | ### Example Requirement
45 |
46 | ```js
47 | {
48 | "type": 1,
49 | "value": ["COMMUNITY"],
50 | "rate": 30,
51 | "ranges": [[3000, 6000]]
52 | }
53 | ```
54 |
55 | ## Priority Structure
56 |
57 | | Field | Type | Description | Required |
58 | | --- | --- | --- | --- |
59 | | `status` | number | [Status](#priority-statuses) of the Priority. | ✅ |
60 | | `name` | string | Name of the Priority. | ✅ |
61 |
62 | ### Example Priority
63 |
64 | ```json
65 | {
66 | "status": 2,
67 | "name": "Discord staff"
68 | }
69 | ```
70 |
71 | ## Detail Structure
72 |
73 | | Field | Type | Description | Required |
74 | | --- | --- | --- | --- |
75 | | `title` | string | Title of the Detail. | ✅ |
76 | | `description` | string | Description (the full text) of the Detail. | ✅ |
77 | | `source` | [Source](#source-structure) | [Source](#source-structure) of the Detail. | ✅ |
78 |
79 | ### Example Detail
80 |
81 | ```json
82 | {
83 | "title": "Status",
84 | "description": "**Nitro Users:** September 2017\n**Non-Nitro Users:** February 2016",
85 | "source": {
86 | "title": "Discord Experiment Hub (Community)",
87 | "link": "https://discord.gg/experiments"
88 | }
89 | }
90 | ```
91 |
92 | ## Source Structure
93 |
94 | | Field | Type | Description | Required |
95 | | --- | --- | --- | --- |
96 | | `title` | string | Title of the Source. | ✅ |
97 | | `link` | string | Link of the Source. | ✅ |
98 |
99 | ### Example Source
100 |
101 | ```json
102 | {
103 | "title": "Discord Experiment Hub (Community)",
104 | "link": "https://discord.gg/experiments"
105 | }
106 | ```
107 |
108 | ## Experiment Types
109 |
110 | | Value | Description |
111 | | --- | --- |
112 | | `0` | Server |
113 | | `1` | User |
114 | | `2` | Server and User |
115 |
116 | ## Rollout Types
117 |
118 | | Value | Description |
119 | | --- | --- |
120 | | `-2` | Replaced by another feature |
121 | | `-1` | Rollout has reverted due to security issues |
122 | | `0` | Regular rollout |
123 | | `1` | Very slow rollout from old to new |
124 | | `3` | In beta/alpha testing |
125 |
126 | ## Requirement Types
127 |
128 | | Value | Description |
129 | | --- | --- |
130 | | `0` | Server must NOT have features |
131 | | `1` | Server must have features |
132 | | `2` | Maximum member count |
133 | | `3` | Member count range |
134 |
135 | ## Priority Statuses
136 |
137 | | Value | Description |
138 | | --- | --- |
139 | | `0` | Not started |
140 | | `1` | Rolling out |
141 | | `2` | Rolled out |
142 |
--------------------------------------------------------------------------------
/experiment-rollout.js:
--------------------------------------------------------------------------------
1 | function experimentRollout() {
2 | return '**This script has been moved to another file. Please update your tag following here: https://github.com/discordexperimenthub/assyst-tags#experiment-rollout**'
3 | };
--------------------------------------------------------------------------------
/experiment-rollout/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "clyde_ai": {
3 | "rate": 12,
4 | "ranges": [
5 | [
6 | 0,
7 | 1200
8 | ]
9 | ],
10 | "experimentType": 0,
11 | "rolloutType": 0,
12 | "requirements": [
13 | {
14 | "type": 0,
15 | "value": [
16 | "COMMUNITY"
17 | ],
18 | "rate": 100,
19 | "ranges": [
20 | [
21 | 0,
22 | 10000
23 | ]
24 | ]
25 | },
26 | {
27 | "type": 2,
28 | "value": 100,
29 | "rate": 11,
30 | "ranges": [
31 | [
32 | 0,
33 | 100
34 | ],
35 | [
36 | 200,
37 | 1200
38 | ]
39 | ]
40 | },
41 | {
42 | "type": 3,
43 | "value": [
44 | 101,
45 | 200
46 | ],
47 | "rate": 1,
48 | "ranges": [
49 | [
50 | 100,
51 | 200
52 | ]
53 | ]
54 | }
55 | ],
56 | "servers": [
57 | {
58 | "name": "Free Clyde 🌌",
59 | "invite": "https://discord.gg/aXMh98DzBj",
60 | "owner": {
61 | "id": "1073325901825187841",
62 | "username": "nerdarp1"
63 | }
64 | },
65 | {
66 | "name": "Clyde Club",
67 | "invite": "https://discord.gg/wMNJmPzdqw",
68 | "owner": {
69 | "id": "760538995649282140",
70 | "username": "vatsious"
71 | }
72 | },
73 | {
74 | "name": "Clyde's Home",
75 | "invite": "https://discord.gg/AB7wgHaq",
76 | "owner": {
77 | "id": "329671025312923648",
78 | "username": "tolgchu"
79 | }
80 | }
81 | ]
82 | },
83 | "split_permissions": {
84 | "experimentType": 0,
85 | "rolloutType": 0,
86 | "rate": 100
87 | },
88 | "channel_summaries": {
89 | "experimentType": 0,
90 | "rolloutType": 3,
91 | "servers": [
92 | {
93 | "name": "Discord Experiment Hub",
94 | "invite": "https://discord.gg/experiments",
95 | "owner": {
96 | "id": "329671025312923648",
97 | "username": "tolgchu"
98 | }
99 | },
100 | {
101 | "name": "SummariesAI",
102 | "invite": "https://discord.gg/tVZHaBMTQE",
103 | "owner": {
104 | "id": "664103194162495488",
105 | "username": "bonkand"
106 | }
107 | },
108 | {
109 | "name": "Discord Previews",
110 | "invite": "https://discord.gg/discord-603970300668805120",
111 | "owner": {
112 | "id": "562415519454461962",
113 | "username": "objectified"
114 | }
115 | },
116 | {
117 | "name": "Wumpus Central",
118 | "invite": "https://discord.gg/wumpus-central-1087801778365546556",
119 | "owner": {
120 | "id": "928302770849710101",
121 | "username": "dzikstar"
122 | }
123 | },
124 | {
125 | "name": "Lucie's community",
126 | "invite": "https://discord.gg/FsnkKkptsq",
127 | "owner": {
128 | "id": "534460774005997571",
129 | "username": "prozilla"
130 | }
131 | }
132 | ]
133 | },
134 | "pomelo": {
135 | "experimentType": 1,
136 | "rolloutType": 1,
137 | "priority": [
138 | {
139 | "status": 2,
140 | "name": "Brands / Large bots | Reserved unless Discord partner for brands"
141 | },
142 | {
143 | "status": 2,
144 | "name": "Discord Staff | Staff and personal accounts"
145 | },
146 | {
147 | "status": 2,
148 | "name": "Partnered / verified / 100+ USD/month server subscription server owners"
149 | },
150 | {
151 | "status": 1,
152 | "name": "Everyone else | Ordered by Nitro/Account creation | Needs Nitro bought before 1st March 2023"
153 | }
154 | ],
155 | "notes": [
156 | {
157 | "title": "Bots",
158 | "text": "Bots are not forced to have unique usernames."
159 | }
160 | ],
161 | "details": [
162 | {
163 | "title": "Announcement",
164 | "description": "Pomelo has **almost** rolled out to everyone. So we aren't following the updates anymore.",
165 | "source": {
166 | "title": "Discord Experiment Hub",
167 | "link": "https://discord.gg/experiments"
168 | }
169 | }
170 | ]
171 | },
172 | "color_together": {
173 | "rolloutType": 0,
174 | "experimentType": 1,
175 | "rate": 100
176 | },
177 | "server_guide": {
178 | "rate": 100,
179 | "experimentType": 0,
180 | "rolloutType": 0
181 | },
182 | "markdown_server": {
183 | "rate": 100,
184 | "ranges": [
185 | [
186 | 0,
187 | 10000
188 | ]
189 | ],
190 | "experimentType": 0,
191 | "rolloutType": 0
192 | },
193 | "pronouns": {
194 | "experimentType": 1,
195 | "rolloutType": 0,
196 | "rate": 100
197 | },
198 | "clyde_dm": {
199 | "experimentType": 1,
200 | "rolloutType": 0,
201 | "rate": 100
202 | },
203 | "channel_emojis": {
204 | "experimentType": 1,
205 | "rolloutType": 0,
206 | "rate": 0,
207 | "notes": [
208 | {
209 | "title": "Other Ways",
210 | "text": "Channel Emojis are enabled by default on Tabs v2 on mobile. It also randomly appears on desktop."
211 | }
212 | ]
213 | },
214 | "media_channels": {
215 | "experimentType": 0,
216 | "rolloutType": 0,
217 | "rate": 100,
218 | "requirements": [
219 | {
220 | "type": 1,
221 | "value": [
222 | "ROLE_SUBSCRIPTIONS_ENABLED"
223 | ],
224 | "rate": 100,
225 | "ranges": [
226 | [
227 | 0,
228 | 10000
229 | ]
230 | ]
231 | }
232 | ],
233 | "servers": [
234 | {
235 | "name": "Media Channels",
236 | "invite": "https://discord.gg/rVKvQBxCYh",
237 | "owner": {
238 | "id": "664103194162495488",
239 | "username": "bonkand"
240 | }
241 | }
242 | ]
243 | },
244 | "remove_reactions_by_emoji": {
245 | "experimentType": 0,
246 | "rolloutType": 0,
247 | "rate": 100
248 | },
249 | "lockdown_mode": {
250 | "experimentType": 0,
251 | "rolloutType": 0,
252 | "rate": 100,
253 | "requirements": [
254 | {
255 | "type": 1,
256 | "value": [
257 | "COMMUNITY"
258 | ],
259 | "rate": 100,
260 | "ranges": [
261 | [
262 | 0,
263 | 10000
264 | ]
265 | ]
266 | }
267 | ]
268 | },
269 | "member_safety_tab": {
270 | "experimentType": 0,
271 | "rolloutType": 0,
272 | "rate": 100
273 | },
274 | "guest_invites": {
275 | "experimentType": 0,
276 | "rolloutType": 0,
277 | "rate": 50,
278 | "ranges": [
279 | [
280 | 100,
281 | 200
282 | ],
283 | [
284 | 600,
285 | 1000
286 | ],
287 | [
288 | 1500,
289 | 2000
290 | ],
291 | [
292 | 3000,
293 | 4000
294 | ],
295 | [
296 | 5000,
297 | 6000
298 | ],
299 | [
300 | 7000,
301 | 8000
302 | ],
303 | [
304 | 9000,
305 | 10000
306 | ]
307 | ],
308 | "requirements": [
309 | {
310 | "type": 2,
311 | "value": 50,
312 | "rate": 50,
313 | "ranges": [
314 | [
315 | 100,
316 | 200
317 | ],
318 | [
319 | 600,
320 | 1000
321 | ],
322 | [
323 | 1500,
324 | 2000
325 | ],
326 | [
327 | 3000,
328 | 4000
329 | ],
330 | [
331 | 5000,
332 | 6000
333 | ],
334 | [
335 | 7000,
336 | 8000
337 | ],
338 | [
339 | 9000,
340 | 10000
341 | ]
342 | ]
343 | }
344 | ],
345 | "servers": [
346 | {
347 | "name": "Guest Invites",
348 | "invite": "https://discord.gg/w6ZRfzrzfe",
349 | "owner": {
350 | "id": "664103194162495488",
351 | "username": "bonkand"
352 | }
353 | }
354 | ]
355 | },
356 | "clyde_personality": {
357 | "experimentType": 0,
358 | "rolloutType": 0,
359 | "rate": 100
360 | },
361 | "clyde_gdm": {
362 | "experimentType": 1,
363 | "rolloutType": 0,
364 | "rate": 50,
365 | "ranges": [
366 | [
367 | 0,
368 | 5000
369 | ]
370 | ]
371 | },
372 | "markdown_user": {
373 | "rate": 100,
374 | "ranges": [
375 | [
376 | 0,
377 | 10000
378 | ]
379 | ],
380 | "experimentType": 1,
381 | "rolloutType": 0,
382 | "notes": [
383 | {
384 | "title": "Masked Links",
385 | "text": "Current rollout doesn't include masked links."
386 | }
387 | ]
388 | },
389 | "automod_usernames": {
390 | "rate": 100,
391 | "experimentType": 0,
392 | "rolloutType": 0,
393 | "requirements": [
394 | {
395 | "type": 1,
396 | "value": [
397 | "COMMUNITY"
398 | ],
399 | "rate": 100,
400 | "ranges": [
401 | [
402 | 0,
403 | 10000
404 | ]
405 | ]
406 | }
407 | ]
408 | },
409 | "voice_channel_topics": {
410 | "experimentType": 0,
411 | "rolloutType": 0,
412 | "rate": 100
413 | },
414 | "reply_welcome_messages": {
415 | "rate": 50,
416 | "experimentType": 0,
417 | "rolloutType": 0,
418 | "ranges": [
419 | [
420 | 5000,
421 | 10000
422 | ]
423 | ]
424 | },
425 | "domain_connection": {
426 | "rate": 100,
427 | "experimentType": 1,
428 | "rolloutType": 0
429 | },
430 | "profile_effects": {
431 | "rate": 0,
432 | "experimentType": 1,
433 | "rolloutType": 0
434 | },
435 | "tabs_v2": {
436 | "rate": 0,
437 | "experimentType": 1,
438 | "rolloutType": 0,
439 | "notes": [
440 | {
441 | "title": "Easter Egg to Enable",
442 | "text": "||1. Tap to this emoji: ✨\n2. Hold the emoji in the popout\n3. Wait a few seconds||"
443 | }
444 | ]
445 | },
446 | "improved_bans_tab": {
447 | "rate": 15,
448 | "experimentType": 0,
449 | "rolloutType": 0,
450 | "ranges": [
451 | [
452 | 100,
453 | 200
454 | ],
455 | [
456 | 600,
457 | 1000
458 | ],
459 | [
460 | 2000,
461 | 3000
462 | ]
463 | ]
464 | },
465 | "server_products": {
466 | "rate": 10,
467 | "experimentType": 0,
468 | "rolloutType": 0,
469 | "ranges": [
470 | [
471 | 0,
472 | 1000
473 | ]
474 | ]
475 | }
476 | }
477 |
--------------------------------------------------------------------------------
/experiment-rollout/index.js:
--------------------------------------------------------------------------------
1 | const lastUpdate = '1694863407'; //Unix timestamp in seconds
2 |
3 | //TESTING STUFF (RUN `npm i node-fetch` BEFORE)
4 | /*
5 | const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
6 |
7 | const message = {
8 | content: '-t rollout pomelo detailed 2'
9 | };
10 |
11 | (async () => {
12 | let o = await experimentRollout('rollout');
13 | console.log(o);
14 | console.log(o.length);
15 | })();
16 | */
17 |
18 | async function experimentRollout(command, override = null) { // `override` IS ONLY FOR DEVELOPMENT
19 | const data = await fetch(`https://raw.githubusercontent.com/discordexperimenthub/assyst-tags/${(override && override !== '') ?? 'main'}/experiment-rollout/data.json`).then(res => res.json());
20 |
21 | let string = message.content.split(`${command} `)[1]?.toLowerCase() ?? '';
22 | let [id, subcommand, index] = string.split(' ');
23 |
24 | if (!id) return `## Usage\n\`-t ${command} \`\n\n## Available Feature Ids\n${Object.keys(data).filter(id => data[id].rate !== 100).map(id => `\`${id}\``).join(', ')}\n\n**Note:** Type \`-t ${command} features\` to see all feature ids including completed ones.\n\n### Last Update\n\n### Contact & Support\n- **Script made by @tolgchu:** \n- **Our Server:** https://discord.gg/experiments`;
25 | else if (id === 'features') return `## All Feature Ids\n${Object.keys(data).map(id => `\`${id}\``).join(', ')}`;
26 |
27 | if (!data[id]) return `❌ This feature id does not exist. Type **\`-t ${command}\`** to see all available feature ids.`;
28 |
29 | let { rate, ranges, experimentType, rolloutType, requirements, priority, notes, timestamp, replacedBy, details, servers } = data[id];
30 | let totalServers = 19000000;
31 | let totalUsers = 150000000;
32 | let count = ((experimentType === 0 ? totalServers : experimentType === 1 ? totalUsers : totalServers + totalUsers) / 100 * rate);
33 |
34 | function fixNumber(n = 0) {
35 | n = n.toString().split('').reverse();
36 |
37 | let fixedNumber = [];
38 | let group = [];
39 | let timer = 0;
40 |
41 | for (var digit of n) {
42 | if (timer === 3) {
43 | fixedNumber.push(group.reverse());
44 |
45 | group = [];
46 | timer = 0;
47 | };
48 |
49 | group.push(digit);
50 |
51 | timer++;
52 | };
53 |
54 | if (group.length > 0) fixedNumber.push(group.reverse());
55 |
56 | return fixedNumber.reverse().map(g => g.map(digit => digit).join('')).join('.');
57 | };
58 |
59 | let fixed = fixNumber(count);
60 |
61 | function priorityStatus(status) {
62 | switch (status) {
63 | case 0:
64 | return '<:unchecked:1078022830828048485>';
65 | case 1:
66 | return '<:dehMiniContributor:1153007062742216855>';
67 | case 2:
68 | return '<:checked:1062424010652123229>';
69 | };
70 | };
71 |
72 | function fixString(t) {
73 | return t.split('_').map(word => word.replace(word.split('').shift(), word.split('').shift().toUpperCase())).join(' ');
74 | };
75 |
76 | let title = fixString(id);
77 | let description = '';
78 |
79 | if (subcommand) {
80 | switch (subcommand) {
81 | case 'detailed':
82 | if (!details) return '❌ This feature does not have any detailed rollout status.';
83 |
84 | const scripts = {};
85 |
86 | let output = [];
87 |
88 | for (var detail of details) {
89 | let evalOutput;
90 |
91 | if (detail.description.startsWith('$js:')) evalOutput = await scripts[detail.description.split(':')[1]]();
92 |
93 | output.push(`## ${detail.title}\n${evalOutput ?? detail.description}\n\n### Source\n- **${detail.source.title}:** <${detail.source.link}>`);
94 | };
95 |
96 | let detailPerPage = 2;
97 | let pages = [];
98 | let newOutput = null;
99 | let limit = Math.ceil(output.length / detailPerPage);
100 |
101 | for (let i = 0; i < limit; i++) {
102 | newOutput = output.splice(0, detailPerPage);
103 | pages.push(newOutput);
104 | };
105 |
106 | if (!index || index <= 0 || index > pages.length) index = 1;
107 |
108 | let pageContent = pages[index - 1]?.map(o => o).join('\n\n') ?? '';
109 |
110 | description = `# ${title} Detailed Rollout Status\n${pageContent}\n\n**Page ${index} of ${pages.length} | \`-t ${command} ${id} detailed \`**\n\n# ⚠️ WARNING!\nAll of these sources are unofficial! Do not completely trust them!`;
111 | break;
112 | case 'servers':
113 | if (!servers) return '❌ This feature does not have any servers added.';
114 |
115 | description = `# Servers With ${title} (${servers.length}/5)\n${servers.map(server => `- **${server.name} by <@${server.owner.id}> (@${server.owner.username}):** <${server.invite}>`).join('\n')}${servers.length < 5 ? `\n\nStill ${5 - servers.length} servers can add here. You can add servers with creating an issue or pull request from our GitHub repository.` : ''}`;
116 | break;
117 | default:
118 | description = '❌ This subcommand does not exist. Available subcommands: \`detailed\`';
119 | };
120 | } else {
121 | switch (rolloutType) {
122 | case -2:
123 | description = `<:switch_accounts:1077291371720867850> This feature has been replaced by **\`${replacedBy}\`** feature.`;
124 | break;
125 | case -1:
126 | description = `<:dehAdmin:1153007058828939424> This feature's rollout has reverted due to some security issues. Restart date is unknown.`;
127 | break;
128 | case 1:
129 | description = `<:ticket:1100811774229495858> This feature is very slowly rolling out to all ${experimentType === 0 ? 'servers' : experimentType === 1 ? 'users' : 'servers and users'} from old to new ones, this process may take a few months.`;
130 | break;
131 | case 3:
132 | description = `<:DEH:1152632213771399288> This feature is currently in beta/alpha testing.`;
133 | break;
134 | default:
135 | switch (rate) {
136 | case 0:
137 | description = `<:DEH:1152632213771399288> This feature has not started to rolling out yet.`;
138 | break;
139 | case 100:
140 | description = `🎉 This feature has rolled out to all ${experimentType === 0 ? 'servers' : experimentType === 1 ? 'users' : 'servers and users'}!`;
141 | break;
142 | default:
143 | description = `<:dehMiniContributor:1153007062742216855> This feature has rolled out to **${rate}%** of all ${experimentType === 0 ? 'servers' : experimentType === 1 ? 'users' : 'servers and users'} (**~${fixed}**)! Ranges: ${ranges.map(range => `\`${range[0] ?? '?'} - ${range[1] ?? '?'}\``).join(', ')}.`;
144 | };
145 | };
146 |
147 | description = `# ${title}\n${description}${(experimentType === 0 && rate !== 100) ? `\n\n## Servers with this feature\n${servers?.length > 0 ? `Type **\`-t ${command} ${id} servers\`** to see the added servers with this feature.` : 'This feature does not have any servers added. You can add with creating an issue or pull request from our GitHub repository.'}` : ''}${priority?.length > 0 ? `\n\n## Rollout Status\n${priority.map(p => `${priorityStatus(p.status)} ${p.name}`).join('\n')}` : ''}${details?.length > 0 ? `\n\n## Detailed Rollout\nThis feature has some detailed rollout status. Type **\`-t ${command} ${id} detailed\`** to see all.` : ''}${requirements?.length > 0 ? `\n\n## Requirements\n${requirements?.map(requirement => `- ${requirement.type === 0 ? `Server must __not__ have ${requirement.value?.map(feature => `\`${feature}\``).join(', ')} feature(s)` : requirement.type === 1 ? `Server must have ${requirement.value?.map(feature => `\`${feature}\``).join(', ')} feature(s)` : requirement.type === 2 ? `Server must have maximum ${requirement.value} members` : `Server must have ${requirement.value[0]}-${requirement.value[1]} members`} for **${requirement.rate}%** (${requirement.ranges?.map(range => `\`${range[0]} - ${range[1]}\``).join(', ')})`).join('\n')}` : ''}${notes?.length > 0 ? `\n\n## Notes\n${notes.map(note => `### ${note.title}\n${note.text}`).join('\n\n')}` : ''}`;
148 | };
149 |
150 | return `${description}\n\n${(Math.floor(Date.now() / 1000) - lastUpdate) > 43200 ? `⚠️ It had been more than 12 hours since the latest update (). If this data is not up-to-date, you can create an issue or pull request from our GitHub repository: ` : `**Last Update: **`}`;
151 | };
152 |
--------------------------------------------------------------------------------
/experiments-api.js:
--------------------------------------------------------------------------------
1 | const baseUrl = 'https://experiments.dscrd.workers.dev';
2 | const warning = 'User experiment rollouts bugged and API is not up-to-date yet! Fix is coming soon:tm:.'; // Warning message to show when the API is down e.g. "The API is down, please try again later"
3 |
4 | function endpoint(path) {
5 | return `${baseUrl}/${path}`;
6 | };
7 |
8 | async function experimentsAPI(command) {
9 | let string = message.content.split(`${command} `)[1]?.toLowerCase() ?? '';
10 |
11 | let argsSlit = string.split(" ");
12 | let [cmd, ...args] = argsSlit;
13 | let raw = false;
14 |
15 | function send(data) {
16 | let two = JSON.stringify(data, 0, 2);
17 | let one = JSON.stringify(data, 0, 1);
18 | let none = JSON.stringify(data);
19 | let limit = 1970;
20 |
21 | if (two.length <= limit) return two;
22 | else if (one.length <= limit) return one;
23 | else return none.slice(0, limit);
24 | };
25 |
26 | switch (cmd) {
27 | case "search":
28 | if (args[0] === "raw") {
29 | args.shift()
30 | raw = true;
31 | };
32 |
33 | let search = args.join(" ") || "search";
34 | let experiments = await fetch(endpoint('experiments')).then(res => res.json());
35 |
36 | return `\`\`\`json\n${send(raw ? search === "search" ? experiments : experiments.filter(e => e.id?.includes(search) || e.title?.toLowerCase()?.includes(search)) : (search === "search" ? experiments : experiments.filter(e => e.id.includes(search))).map(e => e.id))}\n\`\`\`(...)`;
37 | case "get":
38 | if (!args[0]) return ":x: Enter an experiment id";
39 |
40 | let experiment = await fetch(endpoint(`experiments/${args[0]}`)).then(res => res.json()).catch(() => ":x: This experiment does not exists");
41 |
42 | if (typeof experiment === "string") {
43 | let all = await fetch(endpoint('experiments')).then(res => res.json());
44 | let found = all.filter(e => e.id.includes(args[0].toLowerCase()))[0];
45 |
46 | if (found) return `Experiment id does not exists but found this${args[1] ? ` from **\`${found.id}\`**` : ""}:\n\`\`\`json\n${send(args[1] ? found[args[1]] : found)}\n\`\`\``;
47 | else return experiment;
48 | } else {
49 | if (args[1]) {
50 | if (!experiment[args[1]]) return ":x: This field does not exists";
51 | return `\`\`\`json\n${send(experiment[args[1]])}\n\`\`\``;
52 | } else {
53 | return `\`\`\`json\n${send(experiment)}\n\`\`\`**Fields:** ${Object.keys(experiment).map(field => `\`${field}\``).join(", ")}`;
54 | };
55 | };
56 | case "check":
57 | if (!args[0]) return ":x: Enter an experiment id";
58 |
59 | const ids = args[1] === "raw" ? args.slice(2) : args.slice(1);
60 |
61 | if (args[1] === "raw") {
62 | args[1] = null;
63 | raw = true;
64 | };
65 | if (!args[1] && !args[2]) return ":x: Enter server or user id(s)";
66 |
67 | let results = [];
68 |
69 | for (var id of ids) {
70 | if (!results[0]?.includes("exists")) {
71 | let result = await fetch(endpoint(`experiments/check/${args[0]}/${id}`)).then(res => res.json()).catch(() => `❌ This experiment does not exists`);
72 |
73 | if (typeof result === "string") results.push(result);
74 | else {
75 | if (raw) results.push(`**\`${id}:\`**\`\`\`json\n${send(result)}\n\`\`\``);
76 | else results.push(`**\`${id}\`:** ${result.valid ? ":white_check_mark:" : ":x:"} ||debug: ${result.debug}||`);
77 | };
78 | };
79 | };
80 |
81 | return results.map(r => r).join("\n");
82 | default:
83 | return `${warning ? `⚠️ **${warning}**\n\n` : ''}**\`<>\`** Required **|** **\`[]\`** Optional\n\n> **\`-t ${command} search [query]\`:** Searches all experiments and returns ids (put \`raw\` before the \`[query]\` for the raw response)\n> **\`-t ${command} get [field]\`:** Returns an experiment data or a single field of the data\n> **\`-t ${command} check \`:** Checks whether an experiment eligible for the server/user (put \`raw\` before \`\` for raw response)\n\n**API made by \`syndicated#6591\`:** https://experiments.dscrd.workers.dev\n**Script made by \`✨Tolgchu✨#1452\`:** \n\nAnd don't forget to join our server: https://discord.gg/SKVAn3QXJF`;
84 | };
85 | };
--------------------------------------------------------------------------------
/timestamp.js:
--------------------------------------------------------------------------------
1 | function timestamp(tag) {
2 | let string = message.content.split(`${tag} `)[1]?.toLowerCase() ?? '';
3 | let [command] = string.split(' ');
4 |
5 | if (!command) return `## Usage\n\`-t ${tag} \`\n\n## Available Commands\n- **\`now\` -** Get current timestamp.\n- **\`create\` -** Create a timestamp.\n### Contact & Support\n\n- **Script made by @tolgchu**: \n- **Our Server:** https://discord.gg/experiments`;
6 |
7 | let response = '';
8 |
9 | switch (command) {
10 | case 'now':
11 | let milliseconds = Date.now();
12 | let seconds = Math.floor(milliseconds / 1000);
13 |
14 | response = `## Current Timestamp\n\n- **In Seconds:** ${seconds}\n- **In Milliseconds:** ${milliseconds}\n\n## Discord Timestamp Styles\n- **:** \`\`\n- **:** \`\`\n- **:** \`\`\n- **:** \`\`\n- ** (Default):** \`\` or \`\`\n- **:** \`\`\n- **:** \`\``;
15 | break;
16 | case 'create':
17 | let regex = /^\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2}:\d{2})?(?: [+-]\d{2}:\d{2})?$/
18 | let trydate = string.split(`${command} `)[1];
19 |
20 | if (!trydate || !trydate.match(regex)) return `❌ Please enter a valid date!\n- **Usage:** \`-t ${tag} create [] []\`\n- **Example:** \`-t ${tag} create 2011-10-10 14:48:00 +09:00\``;
21 |
22 | let [date, time, zone] = trydate.split(' ');
23 | let milliseconds2 = Date.parse(`${date}${time ? `T${time}${zone ?? ''}` : ''}`);
24 | let seconds2 = Math.floor(milliseconds2 / 1000);
25 |
26 | response = `## Created Timestamp\n\n- **In Seconds:** ${seconds2}\n- **In Milliseconds:** ${milliseconds2}\n\n## Discord Timestamp Styles\n- **:** \`\`\n- **:** \`\`\n- **:** \`\`\n- **:** \`\`\n- ** (Default):** \`\` or \`\`\n- **:** \`\`\n- **:** \`\``;
27 | break;
28 | default:
29 | response = `❌ Unknown command! Please use \`-t ${tag}\` to see all available commands.`;
30 | };
31 |
32 | return response;
33 | };
--------------------------------------------------------------------------------