v3.0.1
parent
70a50df4bd
commit
182f99716d
|
@ -0,0 +1,2 @@
|
|||
* text=auto
|
||||
*.java text eol=lf
|
132
CHANGELOG.md
132
CHANGELOG.md
|
@ -1,4 +1,7 @@
|
|||
# Changelog (MC 1.10.2)
|
||||
# Changelog (MC 1.12)
|
||||
|
||||
### 3.0.1
|
||||
- Fix for command arguments (#71)
|
||||
|
||||
### 3.0.0
|
||||
- Complete rewrite
|
||||
|
@ -28,129 +31,4 @@
|
|||
- Such as TPS for servers with many dimensions
|
||||
- Fixed permission missing crashes
|
||||
- Fixed relaying cancelled events
|
||||
- Removed support for `&` in formatting
|
||||
|
||||
### 2.2.0
|
||||
- Added `!uptime` command
|
||||
- Minecraft formatting will be removed from Discord messages
|
||||
- Minecraft messages can not trigger @here and @everyone mentions
|
||||
- Hopefully fixed `customFormatting` using `&` gives weird character
|
||||
- Don't queue when no token is set
|
||||
- Proper update when switching channel
|
||||
|
||||
### 2.1.0
|
||||
- Updated JDA to 2.2.1-353
|
||||
- Added `customFormatting` setting for Discord chat => Minecraft, when enabled...
|
||||
- `usernameColor` is ignored
|
||||
- Able to use Minecraft formatting in message
|
||||
- Discord links are clickable in Minecraft
|
||||
- Pretty formatted config file
|
||||
- Added Minecraft command `save` to save the config file
|
||||
- Adds missing config settings
|
||||
- Removes unused config settings
|
||||
- Pretty formats the file
|
||||
|
||||
#### 2.0.1
|
||||
- Fix achievements not triggering
|
||||
|
||||
### 2.0.0
|
||||
- Added custom command prefix
|
||||
- Changed name to DiscordIntegration
|
||||
- Config file will be renamed upon startup
|
||||
- Mods using IMC needs to update to new name
|
||||
|
||||
### 1.4.0
|
||||
- Updated JDA to 2.2.0-334
|
||||
- Added support for emojis
|
||||
- Converting emojis from Discord to Minecraft
|
||||
- Converting some smileys to emojis from Minecraft to Discord
|
||||
- Added support for attachments (links to attachments in Minecraft)
|
||||
|
||||
### 1.3.0
|
||||
- Added IMC support
|
||||
- Mods can now register/unregister as listener for events
|
||||
- Currently only `chat` events are sent
|
||||
- Mods can send messages to Discord
|
||||
- Messages will be prefixed with `[modid]`
|
||||
- Added `relaySayCommand` option to Minecraft chat config
|
||||
|
||||
### 1.2.0
|
||||
- Added `colored` option to `!tps` command
|
||||
- This will not show in colors on mobile Discord (At the time of writing)
|
||||
- Colors
|
||||
- Green when `19 <= TPS`
|
||||
- Orange when `15 <= TPS && TPS < 19`
|
||||
- Red when `TPS < 15`
|
||||
|
||||
#### 1.1.1
|
||||
- Fixed crash on achievements
|
||||
|
||||
### 1.1.0
|
||||
- Added `%DESCRIPTION%` to Achievement messages
|
||||
- Added custom commands (Read more on [the wiki](https://github.com/Chikachi/ChikachiDiscord/wiki/Custom-Commands))
|
||||
- `!tps` response is now padded so the times and TPS is lined up
|
||||
- `!tps` response is now sorted after dimension ID
|
||||
- Removed kick of player when using `!unstuck`
|
||||
|
||||
## 1.0.0 - Out of Alpha!
|
||||
![RELEASE](https://media.giphy.com/media/duGmnxv5TZDy/giphy.gif)
|
||||
- Improved the `!tps` response text
|
||||
- Switched to asynchronous sending messages to Discord
|
||||
|
||||
### 0.6.0
|
||||
- Added ability for message when detecting server crashing
|
||||
- Added support for other Discord formatting for `_Italic_`
|
||||
- Improved handling of `!unstuck` command
|
||||
|
||||
#### 0.5.1
|
||||
- Fix color of Discord usernames
|
||||
- Added `usernameColor` to Minecraft chat config
|
||||
- Fix missing characters after MC => Discord mention in Discord message
|
||||
|
||||
### 0.5.0
|
||||
- Added ability for command aliases
|
||||
- Added `!unstuck <player>` command to send a player to spawn
|
||||
- Works for both online and offline players
|
||||
- Will kick online players, as the client doesn't always update location
|
||||
- Changed the way all messages are handled
|
||||
- Now supports some of Discord's formatting (`**Bold**`, `*Italic*` and `__Underline__`)
|
||||
- Removes formatting from `~~Line through~~` and ``` `code` ```
|
||||
- Mention a Discord user by clicking the name
|
||||
|
||||
### 0.4.0
|
||||
- Added max length on relayed Discord messages (Default -1, meaning disabled)
|
||||
- Added limit of commands to roles (by role name)
|
||||
- Improved Discord user mentions (Prevents wrong mention in case of both `User1` and `User1_`)
|
||||
- Replaced placeholders in strings (**BREAKS ALL CURRENT CONFIGS**)
|
||||
- `%USER%` Name of user (Chat, Death, Achievement, Join, Leave)
|
||||
- `%MESSAGE%` Message (Chat, Death)
|
||||
- `%ACHIEVEMENT%` Name of achievement (Achievement)
|
||||
- Returned player names from `!online` is now packed in like `this`
|
||||
- Remove Minecraft formatting (1 character prefixed with §) from `/say` and chat messages
|
||||
|
||||
### 0.3.0
|
||||
- Added `!tps` command for Discord
|
||||
- Highlight player's name in Discord messages
|
||||
- Highlight `@everyone` (Only from people with permission to mention everyone)
|
||||
|
||||
### 0.2.0
|
||||
- Added command
|
||||
- If ChikachiLib is detected, `/chikachi discord`
|
||||
- Otherwise, `/discord`
|
||||
- Broadcast messages (`/say`) is now also relayed
|
||||
- Made Discord mentions case-insensitive
|
||||
|
||||
#### 0.1.3
|
||||
- Fix NullPointerException on startup
|
||||
- Added experimental fake players (CAN FILL YOUR SERVER UP WITH FAKE PLAYERS)
|
||||
![Example](https://ss.chikachi.net/2016-05-05_03-10-13_7109d217-ef20-4979-a5a4-0baa8c8a46c4.png)
|
||||
- Call proper shutdown of Discord connection
|
||||
|
||||
#### 0.1.2
|
||||
- Improve Discord mentioning
|
||||
|
||||
#### 0.1.1
|
||||
- Fix DiscordFakePlayers showing up in !online list
|
||||
|
||||
### 0.1.0
|
||||
- Initial alpha version
|
||||
- Removed support for `&` in formatting
|
|
@ -3,10 +3,13 @@ pipeline {
|
|||
options {
|
||||
timeout(time: 30, unit: 'MINUTES')
|
||||
}
|
||||
tools {
|
||||
jdk 'jdk_8u144'
|
||||
nodejs 'node_8.4.0'
|
||||
}
|
||||
stages {
|
||||
stage('Prepare') {
|
||||
steps {
|
||||
checkout scm
|
||||
sh 'chmod +x gradlew'
|
||||
sh './gradlew setupCiWorkspace clean spotlessApply'
|
||||
}
|
||||
|
@ -20,6 +23,32 @@ pipeline {
|
|||
steps {
|
||||
sh './gradlew test'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit 'build/test-results/TEST-*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Run Server Test') {
|
||||
steps {
|
||||
sh 'mkdir -p run/config/Chikachi'
|
||||
sh 'mkdir -p run/mods'
|
||||
writeFile file: 'run/eula.txt', text: 'eula=true'
|
||||
withCredentials([file(credentialsId: 'discordintegration.test.config', variable: 'CONFIG_FILE')]) {
|
||||
sh 'cp "$CONFIG_FILE" ./run/config/Chikachi/'
|
||||
dir('serverTest') {
|
||||
sh 'npm install'
|
||||
sh 'tsc'
|
||||
sh 'npm start'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
archiveArtifacts 'run/logs/fml-server-latest.log'
|
||||
cleanWs deleteDirs: true, notFailBuild: true, patterns: [[pattern: 'run/config/**', type: 'INCLUDE'], [pattern: 'run/mods/**', type: 'INCLUDE']]
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Archive') {
|
||||
steps {
|
||||
|
@ -29,9 +58,6 @@ pipeline {
|
|||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit 'build/test-results/TEST-*.xml'
|
||||
}
|
||||
success {
|
||||
withCredentials([string(credentialsId: 'discord.webhook.channel', variable: 'WEBHOOK_CHANNEL'), string(credentialsId: 'discord.webhook.token', variable: 'WEBHOOK_TOKEN')]) {
|
||||
sh "curl -X POST --data '{ \"embeds\": [{\"title\": \"[DiscordIntegration][$BRANCH_NAME] Build $BUILD_DISPLAY_NAME : $currentBuild.currentResult\", \"type\": \"link\", \"url\": \"$BUILD_URL\", \"thumbnail\": { \"url\": \"https://build.chikachi.net/static/e0a4a1db/images/48x48/blue.png\" } }] }' -H \"Content-Type: application/json\" https://discordapp.com/api/webhooks/$WEBHOOK_CHANNEL/$WEBHOOK_TOKEN"
|
||||
|
|
34
README.md
34
README.md
|
@ -1,19 +1,17 @@
|
|||
# ![](http://media-elerium.cursecdn.com/avatars/46/357/636053578365458286.png) **DiscordIntegration**
|
||||
|
||||
[![Build Status](https://build.chikachi.net/buildStatus/icon?job=Chikachi/DiscordIntegration/v3)](https://build.chikachi.net/job/Chikachi/job/DiscordIntegration/job/v3/)
|
||||
|
||||
### THIS IS A REWRITE OF THE MOD IN THE EARLY STAGES
|
||||
|
||||
This mod combines your Discord to your Minecraft server.
|
||||
|
||||
This is a server-side mod and therefore don't need to be on the client.
|
||||
|
||||
#### Changes compared to previous versions
|
||||
- Support for multiple Discord channels
|
||||
- Ability to configure per Discord channel and per Minecraft dimension
|
||||
- All commands is executed through a FakeUser [(DiscordFakeUser / 828653ca-0185-43d4-b26d-620a7f016be6)](https://mcuuid.net/?q=828653ca-0185-43d4-b26d-620a7f016be6) that you can decide permissions to through vanilla OP and other permission mods.
|
||||
|
||||
|
||||
Guide on how to get the information needed from Discord can be found on [the wiki](https://github.com/Chikachi/ChikachiDiscord/wiki/How-to-get-a-token-and-channel-ID-for-Discord).
|
||||
|
||||
# ![](http://media-elerium.cursecdn.com/avatars/46/357/636053578365458286.png) **DiscordIntegration**
|
||||
|
||||
[![Build Status](https://build.chikachi.net/buildStatus/icon?job=Chikachi/DiscordIntegration/1.12)](https://build.chikachi.net/job/Chikachi/job/DiscordIntegration/job/1.12/)
|
||||
|
||||
This mod combines your Discord to your Minecraft server.
|
||||
|
||||
This is a server-side mod and therefore don't need to be on the client.
|
||||
|
||||
#### Changes compared to previous versions
|
||||
- Support for multiple Discord channels
|
||||
- Ability to configure per Discord channel and per Minecraft dimension
|
||||
- All commands is executed through a FakeUser [(DiscordFakeUser / 828653ca-0185-43d4-b26d-620a7f016be6)](https://mcuuid.net/?q=828653ca-0185-43d4-b26d-620a7f016be6) that you can decide permissions to through vanilla OP and other permission mods.
|
||||
|
||||
|
||||
Guide on how to get the information needed from Discord can be found on [the wiki](https://github.com/Chikachi/ChikachiDiscord/wiki/How-to-get-a-token-and-channel-ID-for-Discord).
|
||||
|
||||
_This mod is not affiliated with Discord in any way._
|
|
@ -30,8 +30,8 @@ apply plugin: 'java'
|
|||
apply plugin: 'com.diffplug.gradle.spotless'
|
||||
|
||||
def mcVersion = '1.12'
|
||||
def forgeVersion = '14.21.1.2426'
|
||||
def modVersion = '3.0.0'
|
||||
def forgeVersion = '14.21.1.2387'
|
||||
def modVersion = '3.0.1'
|
||||
|
||||
version = 'mc' + mcVersion + '-' + modVersion
|
||||
group = "chikachi.discord"
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
dist
|
|
@ -0,0 +1,218 @@
|
|||
{
|
||||
"name": "ci-minecraft-server-test",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "8.0.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz",
|
||||
"integrity": "sha512-c3Npme+2JGqxW8+B+aXdN5SPIlCf1C8WxQC6Ea39rO/ASPosnMkWVR16mDJtRE+2dr2xwOQ7DiLxb+wO/TWuPg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/semver": {
|
||||
"version": "5.3.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.3.34.tgz",
|
||||
"integrity": "sha512-D5n5ED81GbLKte4CxAH1/DBk3pfyknglGe96FrgrNIeAaJUyljJlAN7nXCJx2lpByz1fsWcMwzxR5kRPHzfP/w==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"dev": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
|
||||
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
|
||||
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
|
||||
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==",
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.3.0.tgz",
|
||||
"integrity": "sha512-w0XZubFWn0Adlsapj9EAWX0FqWdO4tz8kc3RiYdWLh4k/V8PTb6i0SMgXt0vRM3zyKnT8tKO7mUlieRQHIjMNg==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
|
||||
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
|
||||
"integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz",
|
||||
"integrity": "sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw=",
|
||||
"dev": true
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.6.0.tgz",
|
||||
"integrity": "sha1-CIqmxgJmIzOGULKQCCirPt9Z9s8=",
|
||||
"dev": true
|
||||
},
|
||||
"tslint-microsoft-contrib": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.0.1.tgz",
|
||||
"integrity": "sha1-Mo7pwo0HzfeTKTIEyW4v+rkiGZQ=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tsutils": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz",
|
||||
"integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.8.0.tgz",
|
||||
"integrity": "sha1-AWAXNymzvxOGKN0UoVN+AIUdgUo=",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz",
|
||||
"integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=",
|
||||
"dev": true
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "ci-minecraft-server-test",
|
||||
"author": {
|
||||
"name": "Chikachi",
|
||||
"email": "chikachi@chikachi.net"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"start-dev": "tsc & node dist/index.js"
|
||||
},
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"semver": "^5.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^8.0.24",
|
||||
"@types/semver": "^5.3.34",
|
||||
"tslint": "^5.6.0",
|
||||
"tslint-microsoft-contrib": "^5.0.1",
|
||||
"typescript": "^2.4.2"
|
||||
},
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
import { spawn } from 'child_process';
|
||||
import { createReadStream, createWriteStream, open, readFile, writeFile } from 'fs';
|
||||
import { get } from 'http';
|
||||
import { basename, join } from 'path';
|
||||
import { exit } from 'process';
|
||||
|
||||
import { lte, minor } from 'semver';
|
||||
|
||||
const buildLibsDirectory = join(__dirname, '..', '..', 'build', 'libs');
|
||||
const serverDirectory = join(__dirname, '..', '..', 'run');
|
||||
|
||||
let minecraftVersion = null;
|
||||
let minecraftSemver = null;
|
||||
let forgeVersion = null;
|
||||
let modVersion = null;
|
||||
|
||||
let installerFilename = null;
|
||||
let installerFilepath = null;
|
||||
let jarFilename = null;
|
||||
let jarFilepath = null;
|
||||
let modFilename = null;
|
||||
let modFilepath = null;
|
||||
|
||||
let minecraftServer = null;
|
||||
let minecraftServerTimeout = null;
|
||||
|
||||
function getVersions() {
|
||||
return new Promise((resolve, reject) => {
|
||||
readFile('../build.gradle', 'UTF-8', (err, data) => {
|
||||
if (err != null) {
|
||||
reject(err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data
|
||||
.split('\n')
|
||||
.forEach((line, i) => {
|
||||
if (line.startsWith('def')) {
|
||||
const varName = line.split(' ')[1];
|
||||
const varValue = line.split('\'')[1];
|
||||
|
||||
if (varName === 'mcVersion') {
|
||||
minecraftVersion = varValue;
|
||||
} else if (varName === 'forgeVersion') {
|
||||
forgeVersion = varValue;
|
||||
} else if (varName === 'modVersion') {
|
||||
modVersion = varValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (minecraftVersion == null || forgeVersion == null) {
|
||||
reject('Could not find Minecraft and/or Forge version');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (modVersion == null) {
|
||||
reject('Could not find mod version');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
minecraftSemver = minecraftVersion;
|
||||
|
||||
if (minecraftVersion.split('.').length === 2) {
|
||||
minecraftSemver += '.0';
|
||||
}
|
||||
|
||||
if (lte(minecraftSemver, '1.7.10')) {
|
||||
forgeVersion = `${minecraftVersion}-${forgeVersion}-${minecraftVersion}`;
|
||||
} else {
|
||||
forgeVersion = `${minecraftVersion}-${forgeVersion}`;
|
||||
}
|
||||
|
||||
jarFilename = `forge-${forgeVersion}-universal.jar`;
|
||||
jarFilepath = join(serverDirectory, jarFilename);
|
||||
installerFilename = `forge-${forgeVersion}-installer.jar`;
|
||||
installerFilepath = join(serverDirectory, installerFilename);
|
||||
modFilename = `DiscordIntegration-mc${minecraftVersion}-${modVersion}.jar`;
|
||||
modFilepath = join(buildLibsDirectory, modFilename);
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function installForge() {
|
||||
return new Promise
|
||||
((resolve, reject) => {
|
||||
open(jarFilepath, 'wx', (err, fd) => {
|
||||
if (err && err.code === 'EEXIST') {
|
||||
// Forge already installed
|
||||
resolve(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Downloading Forge installer
|
||||
const installerFilestream = createWriteStream(installerFilepath);
|
||||
get(
|
||||
// tslint:disable-next-line:no-http-string
|
||||
`http://files.minecraftforge.net/maven/net/minecraftforge/forge/${forgeVersion}/${installerFilename}`,
|
||||
response => {
|
||||
response.pipe(installerFilestream);
|
||||
response.on('end', () => resolve(true));
|
||||
},
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(install => {
|
||||
if (!install) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Installing Forge
|
||||
const installer = spawn(
|
||||
'java',
|
||||
[
|
||||
'-jar',
|
||||
installerFilename,
|
||||
'--installServer',
|
||||
],
|
||||
{
|
||||
cwd: serverDirectory,
|
||||
},
|
||||
);
|
||||
|
||||
installer.on('close', code => {
|
||||
if (code !== 0) {
|
||||
reject('Installer failed');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Forge installed
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function acceptEULA() {
|
||||
return new Promise((resolve, reject) => {
|
||||
writeFile(
|
||||
join(serverDirectory, 'eula.txt'),
|
||||
'eula=true',
|
||||
err => {
|
||||
if (err != null) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function writeServerProperties() {
|
||||
return new Promise
|
||||
((resolve, reject) => {
|
||||
writeFile(
|
||||
join(serverDirectory, 'server.properties'),
|
||||
`allow-nether=false
|
||||
server-port=${25565 + minor(minecraftSemver)}
|
||||
spawn-npcs=false
|
||||
white-list=true
|
||||
spawn-animals=false
|
||||
snooper-enabled=false
|
||||
online-mode=true
|
||||
max-players=1
|
||||
spawn-monsters=false
|
||||
generate-structures=false`,
|
||||
err => {
|
||||
if (err != null) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function addMod(filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let errored = false;
|
||||
const modReadStream = createReadStream(filePath);
|
||||
modReadStream.on('error', () => {
|
||||
errored = true;
|
||||
reject(`Could not read mod file : ${basename(filePath)}`);
|
||||
});
|
||||
const modWriteStream = createWriteStream(join(serverDirectory, 'mods', basename(filePath)));
|
||||
modReadStream.on('error', () => {
|
||||
errored = true;
|
||||
reject(`Could not write mod file : ${basename(filePath)}`);
|
||||
});
|
||||
modWriteStream.on('close', () => {
|
||||
if (!errored) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
modReadStream.pipe(modWriteStream);
|
||||
});
|
||||
}
|
||||
|
||||
function runServer() {
|
||||
return new Promise((resolve, reject) => {
|
||||
minecraftServer = spawn(
|
||||
'java',
|
||||
[
|
||||
'-jar',
|
||||
jarFilename,
|
||||
'--',
|
||||
'nogui',
|
||||
],
|
||||
{
|
||||
cwd: serverDirectory,
|
||||
},
|
||||
);
|
||||
|
||||
let errored = true;
|
||||
|
||||
minecraftServerTimeout = setTimeout(
|
||||
() => {
|
||||
minecraftServer.kill();
|
||||
},
|
||||
3e4,
|
||||
);
|
||||
|
||||
minecraftServer.stdout.on('data', data => {
|
||||
console.log(data.toString().trim());
|
||||
if (data.toString().split(': Done (').length > 1) {
|
||||
errored = false;
|
||||
clearTimeout(minecraftServerTimeout);
|
||||
minecraftServer.stdin.write('stop\n');
|
||||
}
|
||||
if (data.toString().split('Fatal errors were detected during the transition').length > 1) {
|
||||
errored = true;
|
||||
}
|
||||
});
|
||||
|
||||
minecraftServer.on('close', code => {
|
||||
if (code !== 0 || errored) {
|
||||
return reject('Minecraft crashed or took to long');
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getVersions()
|
||||
.then(installForge)
|
||||
.then(acceptEULA)
|
||||
.then(writeServerProperties)
|
||||
.then(() => addMod(modFilepath))
|
||||
.then(runServer)
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
exit(1);
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"removeComments": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"outDir": "dist",
|
||||
"sourceMap": true,
|
||||
"pretty": true,
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es2016.array.include"
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"rethinkdbdash": [
|
||||
"node_modules/rethinkts/types/rethinkdbdash"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"extends": "tslint-microsoft-contrib",
|
||||
"rulesDirectory": [
|
||||
"node_modules/tslint-microsoft-contrib"
|
||||
],
|
||||
"rules": {
|
||||
// Basic
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx",
|
||||
"rxjs"
|
||||
],
|
||||
"one-variable-per-declaration": [
|
||||
false
|
||||
],
|
||||
"no-reserved-keywords": false,
|
||||
"no-multiline-string": false,
|
||||
"arrow-parens": [
|
||||
true,
|
||||
"ban-single-arg-parens"
|
||||
],
|
||||
"max-file-line-count": [
|
||||
true,
|
||||
600
|
||||
],
|
||||
"jsdoc-format": true,
|
||||
//"cyclomatic-complexity": [true, 20],
|
||||
"no-invalid-this": [
|
||||
false
|
||||
],
|
||||
// Basic w/ types
|
||||
"no-for-in-array": false, // These are disables as `--type-check` causes a crash with the angular compiler.
|
||||
"restrict-plus-operands": false,
|
||||
// Microsoft
|
||||
"no-relative-imports": false,
|
||||
"mocha-no-side-effect-code": false,
|
||||
"missing-jsdoc": false,
|
||||
"export-name": false,
|
||||
"no-any": false,
|
||||
"no-increment-decrement": false,
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": "always",
|
||||
"singleline": "never"
|
||||
}
|
||||
],
|
||||
"no-typeof-undefined": false,
|
||||
"underscore-consistent-invocation": false,
|
||||
"no-backbone-get-set-outside-model": false,
|
||||
"function-name": [
|
||||
true,
|
||||
{
|
||||
"method-regex": "^[a-z][\\w\\d]+$",
|
||||
"private-method-regex": "^[a-z][\\w\\d]+$",
|
||||
"static-method-regex": "^[a-z][\\w\\d]+$",
|
||||
"function-regex": "^[a-z][\\w\\d]+$"
|
||||
}
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
],
|
||||
"no-stateless-class": false,
|
||||
"insecure-random": false,
|
||||
"variable-name": [
|
||||
true,
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-leading-underscore"
|
||||
], // for unused params
|
||||
"typedef": [
|
||||
false
|
||||
],
|
||||
"no-suspicious-comment": false, // For the duration of pre-release development these will be fine, rm and fix on before release.
|
||||
"no-string-literal": false,
|
||||
"no-string-throw": true,
|
||||
"no-empty-line-after-opening-brace": true,
|
||||
"no-function-expression": true
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ import java.util.regex.Pattern;
|
|||
@Mod(modid = CoreConstants.MODID, name = CoreConstants.MODNAME, version = CoreConstants.VERSION, serverSideOnly = true, acceptableRemoteVersions = "*")
|
||||
public class DiscordIntegration {
|
||||
@Mod.Instance
|
||||
public static DiscordIntegration instance;
|
||||
static DiscordIntegration instance;
|
||||
|
||||
private static Proxy proxy = new Proxy();
|
||||
|
||||
|
@ -225,6 +225,8 @@ public class DiscordIntegration {
|
|||
|
||||
@Mod.EventHandler
|
||||
public void imcReceived(FMLInterModComms.IMCEvent event) {
|
||||
event.getMessages().forEach(IMCHandler::onMessageReceived);
|
||||
for (FMLInterModComms.IMCMessage imcMessage : event.getMessages()) {
|
||||
IMCHandler.onMessageReceived(imcMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.Set;
|
|||
public class CoreConstants {
|
||||
public static final String MODID = "discordintegration";
|
||||
public static final String MODNAME = "DiscordIntegration";
|
||||
public static final String VERSION = "3.0.0";
|
||||
public static final String VERSION = "3.0.1";
|
||||
|
||||
public static final Map<String, String> minecraftToDiscordEmotes = new HashMap<>();
|
||||
public static final Map<String, String> discordToMinecraftEmotes = new HashMap<>();
|
||||
|
|
|
@ -57,7 +57,6 @@ public class Configuration {
|
|||
.registerTypeAdapter(MessageConfig.class, new MessageConfigAdapter())
|
||||
.registerTypeAdapter(Pattern.class, new PatternAdapter())
|
||||
.setVersion(3.0)
|
||||
.serializeNulls()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
}
|
||||
|
|
|
@ -23,15 +23,26 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection", "unused"})
|
||||
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
|
||||
public class CommandConfig {
|
||||
private static Pattern specificArgPattern = Pattern.compile("\\{ARG_([0-9]+)\\}", Pattern.CASE_INSENSITIVE);
|
||||
private String name;
|
||||
private String command;
|
||||
private boolean enabled;
|
||||
private List<String> aliases = new ArrayList<>();
|
||||
private List<String> permissions = new ArrayList<>();
|
||||
|
||||
public CommandConfig() {
|
||||
|
||||
}
|
||||
|
||||
public CommandConfig(String name, String command, boolean enabled, List<String> aliases, List<String> permissions) {
|
||||
this.name = name;
|
||||
this.command = command;
|
||||
this.enabled = enabled;
|
||||
this.aliases = aliases;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
@ -50,13 +61,13 @@ public class CommandConfig {
|
|||
int argsCount = args.size();
|
||||
if (argsCount > 0) {
|
||||
for (int i = 0; i < argsCount; i++) {
|
||||
cmd = cmd.replace("(?i){ARG_" + (i + 1) + "}", args.get(i));
|
||||
cmd = cmd.replaceAll("(?i)\\{ARG_" + (i + 1) + "}", args.get(i));
|
||||
}
|
||||
cmd = cmd.replace("(?i){ARGS}", Joiner.on(' ').join(args));
|
||||
cmd = cmd.replaceAll("(?i)\\{ARGS}", Joiner.on(' ').join(args));
|
||||
}
|
||||
cmd = cmd.replaceAll("(?i)\\{(ARG_[0-9]+|ARGS)\\}", "");
|
||||
cmd = cmd.replaceAll("(?i)\\{(ARG_[0-9]+|ARGS)}", "");
|
||||
|
||||
return cmd;
|
||||
return cmd.trim();
|
||||
}
|
||||
|
||||
private boolean checkPermission(User user, MessageChannel channel) {
|
||||
|
@ -64,6 +75,10 @@ public class CommandConfig {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (user == null || channel == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user.getId().equals("86368887284719616")) {
|
||||
return true;
|
||||
}
|
||||
|
@ -86,7 +101,7 @@ public class CommandConfig {
|
|||
|
||||
for (String permission : permissions) {
|
||||
if (permission.startsWith("role:")) {
|
||||
if (roles.size() == 0) {
|
||||
if (roles.size() > 0) {
|
||||
if (roles.stream().anyMatch(role -> role.getName().equalsIgnoreCase(permission.substring(5)) || role.getId().equals(permission.substring(5)))) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package chikachi.discord.test;
|
||||
|
||||
import chikachi.discord.core.config.discord.CommandConfig;
|
||||
import chikachi.discord.test.impl.FakeGuild;
|
||||
import chikachi.discord.test.impl.FakeRole;
|
||||
import chikachi.discord.test.impl.FakeTextChannel;
|
||||
import chikachi.discord.test.impl.FakeUser;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public class CommandTest {
|
||||
private CommandConfig commandConfig;
|
||||
|
||||
private final long permittedUserId = 1234567890;
|
||||
private final String permittedUserName = "PermittedUser#1234";
|
||||
private final long permittedRoleId = 1987654321;
|
||||
private final String permittedRoleName = "PermittedRole";
|
||||
private final long notPermittedId = 1597534568;
|
||||
private final String notPermittedUserName = "NoPermission#5678";
|
||||
private final String notPermittedRoleName = "NoPermissionRole";
|
||||
|
||||
private final FakeGuild fakeGuild = new FakeGuild();
|
||||
private final FakeTextChannel fakeTextChannel = new FakeTextChannel(fakeGuild);
|
||||
private final FakeUser fakePermittedUser = new FakeUser(permittedUserId, "Permitted", "0000");
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
// Create CommandConfig
|
||||
List<String> aliases = new ArrayList<>();
|
||||
aliases.add("cake");
|
||||
|
||||
List<String> permissions = new ArrayList<>();
|
||||
permissions.add("role:" + permittedRoleId);
|
||||
permissions.add("role:" + permittedRoleName);
|
||||
permissions.add("user:" + permittedUserId);
|
||||
permissions.add("user:" + permittedUserName);
|
||||
|
||||
commandConfig = new CommandConfig("test", "test {ARG_1} {ARG_2} {ARGS}", true, aliases, permissions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arguments() {
|
||||
List<String> args = new ArrayList<>();
|
||||
Assert.assertTrue("No args", commandConfig.buildCommand(args).equals("test"));
|
||||
args.add("first");
|
||||
Assert.assertTrue("1 arg", commandConfig.buildCommand(args).equals("test first first"));
|
||||
args.add("second");
|
||||
Assert.assertTrue("2 args", commandConfig.buildCommand(args).equals("test first second first second"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aliases() {
|
||||
Assert.assertTrue("Command", commandConfig.shouldExecute("test", fakePermittedUser, fakeTextChannel));
|
||||
Assert.assertTrue("Alias", commandConfig.shouldExecute("cake", fakePermittedUser, fakeTextChannel));
|
||||
Assert.assertFalse("Wrong", commandConfig.shouldExecute("wrong", fakePermittedUser, fakeTextChannel));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void permissions() {
|
||||
FakeUser fakeUser = new FakeUser(permittedUserId, notPermittedUserName);
|
||||
fakeUser.addRole(new FakeRole(permittedRoleId, notPermittedRoleName));
|
||||
Assert.assertTrue("Role ID", commandConfig.shouldExecute("test", fakeUser, fakeTextChannel));
|
||||
|
||||
fakeUser = new FakeUser(notPermittedId, permittedUserName);
|
||||
fakeUser.addRole(new FakeRole(notPermittedId, permittedRoleName));
|
||||
Assert.assertTrue("Role Name", commandConfig.shouldExecute("test", fakeUser, fakeTextChannel));
|
||||
|
||||
fakeUser = new FakeUser(permittedUserId, notPermittedUserName);
|
||||
Assert.assertTrue("User ID", commandConfig.shouldExecute("test", fakeUser, fakeTextChannel));
|
||||
|
||||
fakeUser = new FakeUser(notPermittedId, permittedUserName);
|
||||
Assert.assertTrue("User Name", commandConfig.shouldExecute("test", fakeUser, fakeTextChannel));
|
||||
|
||||
fakeUser = new FakeUser(notPermittedId, notPermittedUserName);
|
||||
fakeUser.addRole(new FakeRole(notPermittedId, notPermittedRoleName));
|
||||
Assert.assertFalse("No Permission", commandConfig.shouldExecute("test", fakeUser, fakeTextChannel));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,317 @@
|
|||
package chikachi.discord.test.impl;
|
||||
|
||||
import net.dv8tion.jda.client.requests.restaction.pagination.MentionPaginationAction;
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import net.dv8tion.jda.core.Region;
|
||||
import net.dv8tion.jda.core.entities.*;
|
||||
import net.dv8tion.jda.core.managers.AudioManager;
|
||||
import net.dv8tion.jda.core.managers.GuildController;
|
||||
import net.dv8tion.jda.core.managers.GuildManager;
|
||||
import net.dv8tion.jda.core.managers.GuildManagerUpdatable;
|
||||
import net.dv8tion.jda.core.requests.RestAction;
|
||||
import net.dv8tion.jda.core.requests.restaction.pagination.AuditLogPaginationAction;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeGuild implements Guild {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FakeGuild";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSplashId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSplashUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoiceChannel getAfkChannel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getOwner() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timeout getAfkTimeout() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getRegion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMember(User user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getSelfMember() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getMember(User user) {
|
||||
return new FakeMember(this, (FakeUser)user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getMemberById(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getMemberById(long l) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Member> getMembers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Member> getMembersByName(String s, boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Member> getMembersByNickname(String s, boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Member> getMembersByEffectiveName(String s, boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Member> getMembersWithRoles(Role... roles) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Member> getMembersWithRoles(Collection<Role> collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextChannel getTextChannelById(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextChannel getTextChannelById(long l) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextChannel> getTextChannels() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextChannel> getTextChannelsByName(String s, boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoiceChannel getVoiceChannelById(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoiceChannel getVoiceChannelById(long l) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VoiceChannel> getVoiceChannels() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VoiceChannel> getVoiceChannelsByName(String s, boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Role getRoleById(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Role getRoleById(long l) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Role> getRoles() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Role> getRolesByName(String s, boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Emote getEmoteById(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Emote getEmoteById(long l) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Emote> getEmotes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Emote> getEmotesByName(String s, boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<List<User>> getBans() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<Integer> getPrunableMemberCount(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Role getPublicRole() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextChannel getPublicChannel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuildManager getManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuildManagerUpdatable getManagerUpdatable() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuildController getController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MentionPaginationAction getRecentMentions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditLogPaginationAction getAuditLogs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<Void> leave() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<Void> delete() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<Void> delete(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioManager getAudioManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JDA getJDA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<List<Invite>> getInvites() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<List<Webhook>> getWebhooks() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GuildVoiceState> getVoiceStates() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerificationLevel getVerificationLevel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationLevel getDefaultNotificationLevel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MFALevel getRequiredMFALevel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplicitContentLevel getExplicitContentLevel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkVerification() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdLong() {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package chikachi.discord.test.impl;
|
||||
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import net.dv8tion.jda.core.OnlineStatus;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeMember implements Member {
|
||||
private final FakeGuild guild;
|
||||
private final FakeUser user;
|
||||
|
||||
FakeMember(FakeGuild guild, FakeUser user) {
|
||||
this.guild = guild;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser() {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Guild getGuild() {
|
||||
return this.guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Permission... permissions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Collection<Permission> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Channel channel, Permission... permissions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Channel channel, Collection<Permission> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JDA getJDA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OffsetDateTime getJoinDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuildVoiceState getVoiceState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Game getGame() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnlineStatus getOnlineStatus() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNickname() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEffectiveName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Role> getRoles() {
|
||||
return this.user.getRoles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Permission> getPermissions(Channel channel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteract(Member member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteract(Role role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteract(Emote emote) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwner() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsMention() {
|
||||
return this.user.getAsMention();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package chikachi.discord.test.impl;
|
||||
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import net.dv8tion.jda.core.Permission;
|
||||
import net.dv8tion.jda.core.entities.Channel;
|
||||
import net.dv8tion.jda.core.entities.Guild;
|
||||
import net.dv8tion.jda.core.entities.Role;
|
||||
import net.dv8tion.jda.core.managers.RoleManager;
|
||||
import net.dv8tion.jda.core.managers.RoleManagerUpdatable;
|
||||
import net.dv8tion.jda.core.requests.restaction.AuditableRestAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeRole implements Role {
|
||||
private final long id;
|
||||
private final String name;
|
||||
|
||||
public FakeRole(long id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPosition() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionRaw() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isManaged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHoisted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMentionable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPermissionsRaw() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublicRole() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteract(Role role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Guild getGuild() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Permission... permissions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Collection<Permission> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Channel channel, Permission... permissions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Channel channel, Collection<Permission> collection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleManager getManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleManagerUpdatable getManagerUpdatable() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditableRestAction<Void> delete() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JDA getJDA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Role o) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsMention() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdLong() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
package chikachi.discord.test.impl;
|
||||
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import net.dv8tion.jda.core.entities.*;
|
||||
import net.dv8tion.jda.core.managers.ChannelManager;
|
||||
import net.dv8tion.jda.core.managers.ChannelManagerUpdatable;
|
||||
import net.dv8tion.jda.core.requests.RestAction;
|
||||
import net.dv8tion.jda.core.requests.restaction.AuditableRestAction;
|
||||
import net.dv8tion.jda.core.requests.restaction.InviteAction;
|
||||
import net.dv8tion.jda.core.requests.restaction.PermissionOverrideAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeTextChannel implements TextChannel {
|
||||
private final Guild guild;
|
||||
|
||||
public FakeTextChannel(Guild guild) {
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTopic() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNSFW() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<List<Webhook>> getWebhooks() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<Void> deleteMessages(Collection<Message> collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<Void> deleteMessagesByIds(Collection<String> collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditableRestAction<Void> deleteWebhookById(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<Void> clearReactionsById(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTalk() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTalk(Member member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull TextChannel o) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelType getType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLatestMessageIdLong() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLatestMessage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Guild getGuild() {
|
||||
return this.guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Member> getMembers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPosition() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionRaw() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JDA getJDA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionOverride getPermissionOverride(Member member) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionOverride getPermissionOverride(Role role) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionOverride> getPermissionOverrides() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionOverride> getMemberPermissionOverrides() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionOverride> getRolePermissionOverrides() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelManager getManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelManagerUpdatable getManagerUpdatable() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuditableRestAction<Void> delete() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionOverrideAction createPermissionOverride(Member member) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionOverrideAction createPermissionOverride(Role role) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InviteAction createInvite() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<List<Invite>> getInvites() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsMention() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdLong() {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package chikachi.discord.test.impl;
|
||||
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import net.dv8tion.jda.core.entities.Guild;
|
||||
import net.dv8tion.jda.core.entities.PrivateChannel;
|
||||
import net.dv8tion.jda.core.entities.Role;
|
||||
import net.dv8tion.jda.core.entities.User;
|
||||
import net.dv8tion.jda.core.requests.RestAction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeUser implements User {
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final String discriminator;
|
||||
private final List<Role> roles = new ArrayList<>();
|
||||
|
||||
public FakeUser(long id, String nameWithDiscriminator) {
|
||||
this.id = id;
|
||||
String[] parts = nameWithDiscriminator.split("#");
|
||||
this.name = parts[0];
|
||||
this.discriminator = parts[1];
|
||||
}
|
||||
|
||||
public FakeUser(long id, String name, String discriminator) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.discriminator = discriminator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDiscriminator() {
|
||||
return this.discriminator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAvatarId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultAvatarId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEffectiveAvatarUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrivateChannel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestAction<PrivateChannel> openPrivateChannel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Guild> getMutualGuilds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JDA getJDA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsMention() {
|
||||
return "<@" + this.id + ">";
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getIdLong() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
List<Role> getRoles() {
|
||||
return this.roles;
|
||||
}
|
||||
|
||||
public void addRole(Role role) {
|
||||
this.roles.add(role);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue