├── .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 | Creative Commons License
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 | }; --------------------------------------------------------------------------------