├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── api
├── .gitignore
├── gradle
│ └── dependency-locks
│ │ ├── annotationProcessor.lockfile
│ │ ├── archives.lockfile
│ │ ├── compile.lockfile
│ │ ├── compileClasspath.lockfile
│ │ ├── compileOnly.lockfile
│ │ ├── default.lockfile
│ │ ├── jacocoAgent.lockfile
│ │ ├── jacocoAnt.lockfile
│ │ ├── runtime.lockfile
│ │ ├── runtimeClasspath.lockfile
│ │ ├── testAnnotationProcessor.lockfile
│ │ ├── testCompile.lockfile
│ │ ├── testCompileClasspath.lockfile
│ │ ├── testCompileOnly.lockfile
│ │ ├── testRuntime.lockfile
│ │ └── testRuntimeClasspath.lockfile
└── src
│ ├── main
│ └── java
│ │ ├── net
│ │ └── dv8tion
│ │ │ └── jda
│ │ │ └── api
│ │ │ └── audio
│ │ │ ├── AudioSendHandler.java
│ │ │ ├── factory
│ │ │ ├── IAudioSendFactory.java
│ │ │ ├── IAudioSendSystem.java
│ │ │ └── IPacketProvider.java
│ │ │ └── hooks
│ │ │ └── ConnectionStatus.java
│ │ └── space
│ │ └── npstr
│ │ └── magma
│ │ └── api
│ │ ├── MagmaApi.java
│ │ ├── MdcKey.java
│ │ ├── Member.java
│ │ ├── ServerUpdate.java
│ │ ├── SpeakingMode.java
│ │ ├── WebsocketConnectionState.java
│ │ ├── event
│ │ ├── ImmutableApiEvent.java
│ │ ├── MagmaEvent.java
│ │ ├── WebSocketClosed.java
│ │ └── package-info.java
│ │ └── package-info.java
│ └── test
│ └── java
│ └── space
│ └── npstr
│ └── magma
│ └── api
│ ├── MemberTest.java
│ └── ServerUpdateTest.java
├── build.gradle
├── gradle
├── dependency-locks
│ ├── annotationProcessor.lockfile
│ ├── archives.lockfile
│ ├── buildscript-classpath.lockfile
│ ├── compile.lockfile
│ ├── compileClasspath.lockfile
│ ├── compileOnly.lockfile
│ ├── default.lockfile
│ ├── jacocoAgent.lockfile
│ ├── jacocoAnt.lockfile
│ ├── runtime.lockfile
│ ├── runtimeClasspath.lockfile
│ ├── testAnnotationProcessor.lockfile
│ ├── testCompile.lockfile
│ ├── testCompileClasspath.lockfile
│ ├── testCompileOnly.lockfile
│ ├── testRuntime.lockfile
│ └── testRuntimeClasspath.lockfile
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── impl
├── .gitignore
├── gradle
│ └── dependency-locks
│ │ ├── annotationProcessor.lockfile
│ │ ├── archives.lockfile
│ │ ├── compile.lockfile
│ │ ├── compileClasspath.lockfile
│ │ ├── compileOnly.lockfile
│ │ ├── default.lockfile
│ │ ├── jacocoAgent.lockfile
│ │ ├── jacocoAnt.lockfile
│ │ ├── runtime.lockfile
│ │ ├── runtimeClasspath.lockfile
│ │ ├── testAnnotationProcessor.lockfile
│ │ ├── testCompile.lockfile
│ │ ├── testCompileClasspath.lockfile
│ │ ├── testCompileOnly.lockfile
│ │ ├── testRuntime.lockfile
│ │ └── testRuntimeClasspath.lockfile
└── src
│ ├── main
│ └── java
│ │ ├── com
│ │ └── iwebpp
│ │ │ └── crypto
│ │ │ └── TweetNaclFast.java
│ │ ├── net
│ │ └── dv8tion
│ │ │ └── jda
│ │ │ └── api
│ │ │ └── audio
│ │ │ └── AudioPacket.java
│ │ └── space
│ │ └── npstr
│ │ └── magma
│ │ └── impl
│ │ ├── AudioStack.java
│ │ ├── AudioStackLifecyclePipeline.java
│ │ ├── EncryptionMode.java
│ │ ├── Magma.java
│ │ ├── MagmaVersionProvider.java
│ │ ├── connections
│ │ ├── AudioConnection.java
│ │ ├── AudioWebSocket.java
│ │ ├── AudioWebSocketSessionHandler.java
│ │ ├── hax
│ │ │ ├── ClosingReactorNettyWebSocketClient.java
│ │ │ ├── ClosingReactorNettyWebSocketSession.java
│ │ │ ├── ClosingWebSocketClient.java
│ │ │ └── package-info.java
│ │ └── package-info.java
│ │ ├── events
│ │ ├── audio
│ │ │ ├── conn
│ │ │ │ ├── ConnectionEvent.java
│ │ │ │ ├── SetEncryptionMode.java
│ │ │ │ ├── SetSecretKey.java
│ │ │ │ ├── SetSsrc.java
│ │ │ │ ├── SetTargetAddress.java
│ │ │ │ ├── Shutdown.java
│ │ │ │ ├── UpdateSendHandler.java
│ │ │ │ ├── UpdateSpeaking.java
│ │ │ │ └── package-info.java
│ │ │ ├── lifecycle
│ │ │ │ ├── CloseWebSocket.java
│ │ │ │ ├── ConnectWebSocket.java
│ │ │ │ ├── LifecycleEvent.java
│ │ │ │ ├── Shutdown.java
│ │ │ │ ├── UpdateSendHandler.java
│ │ │ │ ├── UpdateSpeakingMode.java
│ │ │ │ ├── VoiceServerUpdate.java
│ │ │ │ └── package-info.java
│ │ │ └── ws
│ │ │ │ ├── CloseCode.java
│ │ │ │ ├── OpCode.java
│ │ │ │ ├── Speaking.java
│ │ │ │ ├── WsEvent.java
│ │ │ │ ├── in
│ │ │ │ ├── ClientDisconnect.java
│ │ │ │ ├── HeartbeatAck.java
│ │ │ │ ├── Hello.java
│ │ │ │ ├── Ignored.java
│ │ │ │ ├── InboundWsEvent.java
│ │ │ │ ├── Ready.java
│ │ │ │ ├── Resumed.java
│ │ │ │ ├── SessionDescription.java
│ │ │ │ ├── Unknown.java
│ │ │ │ ├── WebSocketClosed.java
│ │ │ │ └── package-info.java
│ │ │ │ ├── out
│ │ │ │ ├── Heartbeat.java
│ │ │ │ ├── Identify.java
│ │ │ │ ├── OutboundWsEvent.java
│ │ │ │ ├── Resume.java
│ │ │ │ ├── SelectProtocol.java
│ │ │ │ └── package-info.java
│ │ │ │ └── package-info.java
│ │ └── package-info.java
│ │ ├── immutables
│ │ ├── ImmutableLcEvent.java
│ │ ├── ImmutableWsEvent.java
│ │ ├── SessionInfo.java
│ │ └── package-info.java
│ │ ├── package-info.java
│ │ └── processing
│ │ ├── PacketProvider.java
│ │ ├── PacketUtil.java
│ │ └── package-info.java
│ └── test
│ └── java
│ └── space
│ └── npstr
│ └── magma
│ └── impl
│ └── EncryptionModeTest.java
├── jitpack.yml
├── platform
├── .gitignore
└── gradle
│ └── dependency-locks
│ ├── archives.lockfile
│ ├── classpath.lockfile
│ ├── default.lockfile
│ ├── jacocoAgent.lockfile
│ └── jacocoAnt.lockfile
├── settings.gradle
└── src
└── main
└── java
└── space
└── npstr
└── magma
└── MagmaFactory.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # gradle
2 | /build/
3 | /.gradle/
4 | gradle.properties
5 |
6 | # eclipse files
7 | .classpath
8 | .project
9 | .settings/
10 | /bin/
11 |
12 | # intellij files
13 | /.idea/
14 | out/*
15 | *.iml
16 | *.ipr
17 | *.iws
18 |
19 | # netbeans files
20 | /.nb-gradle/
21 | *.nb-gradle-properties
22 |
23 | # dolphin
24 | .directory
25 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | addons:
4 | sonarcloud:
5 | organization: "napstr-github"
6 | token:
7 | secure: "lWokv1JtxppNnKuf9U7UGBAMEAN9pK0hO+DNMujMHoU5B18+L/aTi5jBycTWji8dlAVkEMN87uDl0xooLc//wT1b42kI3R0j0oMJuUGUf/pTpqYUomEHs3rfUPCIKqqtUPln1qbMghJ0c6LOQzYTPOELKH66q1Hm+byvDSuFcljPKqqgQZRBtIN4u2ekJRIXqBoeDqoBf9MyxMWtNcDivmLVaLFIxuvPxaK+7aiKlul505MjfVb2uLy79fAfejfoz8VVX5lqF4pJraG8lhrFytMuDEa4ib4ICPXhDP1PapE7RPOLHdQq4HTrV//Xa7dTdTJh0g71I2NEiPTQEcW107LeEY2CVO3MVolrfulzl3vL79up5m5HI6TQCSfKM4LzdZ2mBIN87lg+3PO0v4FnXpjHWIlcz62nKfsjNHm1FeWIbhsqfHULh8Ugv688K0PmCnX0/2EYTVtNFo5ULSPNSFgPN3IB38HkjLAQp+uDMI4OEgInwDf/dutjd6AdjSb0e77ArQ28p5dHJCVSMAqOB/9s0SaLgaw4egy9mZpIgUIvlPca1Im85A1SHs6gmQk72TOnt2L/1NsCEyHIoU9jdU+nRghsaOUdHBSqvS7ZUXDIoRfrfZy2/PCCC3lIfT6vyhKdYtzsou4z8eebHXTQqk10mWHZMeowxfa5JcuwazE="
8 |
9 | env:
10 | global:
11 | - BUILD_NUMBER: "$TRAVIS_BUILD_NUMBER"
12 |
13 | cache:
14 | directories:
15 | - "$HOME/.m2"
16 | - "$HOME/.gradle/wrapper"
17 | - "$HOME/.gradle/caches"
18 | - "$HOME/.sonar/cache"
19 |
20 | stages:
21 | - build & test
22 | - sonar
23 |
24 | jobs:
25 | fast_finish: true
26 | allow_failures:
27 | - jdk: openjdk-ea
28 | include:
29 | - stage: build & test
30 | jdk: openjdk8
31 | script:
32 | - "java -Xmx32m -version"
33 | - "./gradlew build --info"
34 |
35 | - stage: build & test
36 | jdk: openjdk11
37 | script:
38 | - "java -Xmx32m -version"
39 | - "./gradlew build --info"
40 |
41 | - stage: build & test
42 | jdk: openjdk12
43 | script:
44 | - "java -Xmx32m -version"
45 | - "./gradlew build --info"
46 |
47 | - stage: build & test
48 | jdk: openjdk-ea
49 | script:
50 | - "java -Xmx32m -version"
51 | - "./gradlew build --info"
52 |
53 |
54 | - stage: sonar
55 | jdk: openjdk11
56 | before_script:
57 | #for full sonar cloud blame information
58 | - "git fetch --unshallow"
59 | script:
60 | - "./gradlew sonarqube"
61 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Changelog
2 |
3 | Expect breaking changes between minor versions while v1 has not been released.
4 |
5 | ### v0.12.6
6 | - Allow configuring SSL options
7 |
8 | ### v0.12.5
9 | - Add some more logging for errors in the websocket connection
10 |
11 | ### v0.12.4
12 | - Switch to "modern" echo message format [#25](https://github.com/napstr/Magma/pull/25)
13 | - Adhere to Discord's user agent format [#26](https://github.com/napstr/Magma/pull/26)
14 | - Build on jitpack with java8 [#27](https://github.com/napstr/Magma/pull/27)
15 |
16 | ### v0.12.3
17 | - Send a basic user agent header when connecting to Discord
18 |
19 | ### v0.12.2
20 | - Fixed jitpack versioning troubles. Previous versions might not resolve correctly, use this one instead.
21 |
22 | ### v0.12.0
23 | - Align versions with Spring Boot's dependency management (may result in versions of transitive dependencies going up / down)
24 | - Internal: Dependency locking & version ranges
25 |
26 | ### v0.11.0
27 |
28 | - Picked up the commits from [Minn's Magma fork](https://github.com/MinnDevelopment/Magma):
29 |
30 | - 0.10.4: Fix xsalsa20_poly1305 encryption (legacy support)
31 | - 0.10.3: Convert to single DatagramSocket shared by all audio pipelines
32 | - 0.10.2: Fix possible memory leak due to unclosed datagram sockets
33 | - 0.10.1: Fix incorrect handling of ByteBuffer in asDatagramPacket
34 | - 0.10.0: Restructure for JDA V4 compatibility
35 | - 0.9.2: Fix handling of 4014 close code
36 | - 0.9.1:
37 | - Add speaking modes
38 | - Change transitive dependencies to proper api/implementation scopes
39 | - Fix several javadoc errors
40 | - 0.9.0: Support Java 8 through 10
41 |
42 |
43 | ### ~~v0.10.0~~
44 |
45 | This version has been skipped to avoid confusion with versions from [Minn's Magma fork](https://github.com/MinnDevelopment/Magma).
46 | Minn's improvements and updates have been incorporated in the next minor version.
47 |
48 |
49 | ### v0.9.0
50 | - Dependencies bumped
51 | - **Breaking**: Reorganized the project into `api` and `impl` modules, resulting in new Maven/Gradle coordinates and new packages of the Magma classes
52 | Click me
53 |
54 | `space.npstr.magma.MagmaApi` -> `space.npstr.magma.api.MagmaApi`
55 | `space.npstr.magma.MdcKey` -> `space.npstr.magma.api.MdcKey`
56 | `space.npstr.magma.Member` -> `space.npstr.magma.api.Member`
57 | `space.npstr.magma.MagmaMember` -> `space.npstr.magma.api.MagmaMember`
58 | `space.npstr.magma.ServerUpdate` -> `space.npstr.magma.api.ServerUpdate`
59 | `space.npstr.magma.MagmaServerUpdate` -> `space.npstr.magma.api.MagmaServerUpdate`
60 | `space.npstr.magma.WebsocketConnectionState` -> `space.npstr.magma.api.WebsocketConnectionState`
61 | `space.npstr.magma.MagmaWebsocketConnectionState` -> `space.npstr.magma.api.MagmaWebsocketConnectionState`
62 | `space.npstr.magma.events.api.MagmaEvent` -> `space.npstr.magma.api.event.MagmaEvent`
63 | `space.npstr.magma.events.api.WebSocketClosed` -> `space.npstr.magma.api.event.WebSocketClosed`
64 | `space.npstr.magma.events.api.WebSocketClosedApiEvent` -> `space.npstr.magma.api.event.WebSocketClosedApiEvent`
65 | `MagmaApi.of` -> `MagmaFactory.of`
66 |
67 |
68 |
69 | ### v0.8.3
70 | - Fix bug with reconnecting in the same guild introduced in 0.8.2
71 |
72 | ### v0.8.2
73 | - Idempotent handling of connection requests [\#16](https://github.com/napstr/Magma/pull/16) (thanks @Frederikam)
74 |
75 | ### v0.8.1
76 | - Fix jitpack build
77 |
78 | ### v0.8.0
79 | - Add a WebsocketConnectionState to report the state of the websocket connections managed by a MagmaApi
80 |
81 | ### v0.7.0
82 | - Bump dependencies, including Java 11.
83 |
84 | ### v0.6.0
85 | - Introduce `MagmaApi#getEventStream` that allows user code to consume events from Magma, for example when the
86 | websocket is closed.
87 |
88 | ### v0.5.0
89 | - Port the remaining changes of JDA 3.7 (see [\#651](https://github.com/DV8FromTheWorld/JDA/pull/651)),
90 | notably the switch from `byte[]`s to `ByteBuffer`s in most places. This includes a backwards incompatible change to
91 | `IPacketProvider`, marking it as a non-threadsafe class.
92 | **Known issues:** Applications using [japp](https://github.com/Shredder121/jda-async-packetprovider)
93 | 1.2 or below have broken audio output.
94 |
95 | ### v0.4.5
96 | - Use direct byte buffers (off heap) for Undertow
97 |
98 | ### v0.4.4
99 | - Send our SSRC along with OP 5 Speaking updates
100 | - Add MDC and more trace logs to enable better reporting of issues
101 | - Update close code handling for expected 1xxx codes and warnings on suspicious closes
102 | - Deal with Opus interpolation
103 |
104 | ### v0.4.3
105 | - Fix 4003s closes due to sending events before identifying
106 |
107 | ### v0.4.0
108 | - Opus conversion removed. Send handlers are expected to provide opus packets.
109 | - Fix for a possible leak of send handlers
110 |
111 | ### v0.3.3
112 | - Fully event based AudioConnection
113 | - Correct Schedulers used for event processing
114 | - Dependency updates
115 | - Code quality improvements via SonarCloud
116 |
117 | ### v0.3.2
118 | - Log endpoint to which the connection has been closed along with the reason
119 | - Share a BufferPool between all connections to avoid memory leak
120 |
121 | ### v0.3.1
122 | - Dependency updates
123 | - Additional experimental build against Java 11
124 |
125 | ### v0.3.0
126 | - Type and parameter safety in the Api by introducing a simple DSL
127 |
128 | ### v0.2.1
129 | - Handle op 14 events
130 |
131 | ### v0.2.0
132 | - Build with java 10
133 |
134 | ### v0.1.2
135 | - Implement v4 of Discords Voice API
136 |
137 | ### v0.1.1
138 | - Depend on opus-java through jitpack instead of a git submodule
139 |
140 | ### v0.1.0
141 | - Ignore more irrelevant events
142 | - Smol docs update
143 | - Licensed as Apache 2.0
144 | - Use IP provided by Discord instead of endpoint address for the UDP connection
145 |
146 | ### v0.0.1
147 | - It's working, including resumes.
148 |
--------------------------------------------------------------------------------
/api/.gitignore:
--------------------------------------------------------------------------------
1 | build/
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/annotationProcessor.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.immutables:value:2.8.3
5 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
6 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/archives.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/compile.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/compileClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.github.spotbugs:spotbugs-annotations:4.0.1
5 | com.google.code.findbugs:jsr305:3.0.2
6 | io.projectreactor:reactor-core:3.3.4.RELEASE
7 | org.immutables:value:2.8.3
8 | org.reactivestreams:reactive-streams:1.0.3
9 | org.slf4j:slf4j-api:1.7.30
10 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
11 | org.springframework:spring-beans:5.2.5.RELEASE
12 | org.springframework:spring-core:5.2.5.RELEASE
13 | org.springframework:spring-jcl:5.2.5.RELEASE
14 | org.springframework:spring-web:5.2.5.RELEASE
15 | org.springframework:spring-webflux:5.2.5.RELEASE
16 | space.npstr:annotations:0.0.2
17 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/compileOnly.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.github.spotbugs:spotbugs-annotations:4.0.1
5 | com.google.code.findbugs:jsr305:3.0.2
6 | org.immutables:value:2.8.3
7 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
8 | space.npstr:annotations:0.0.2
9 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/default.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | io.projectreactor:reactor-core:3.3.4.RELEASE
5 | org.reactivestreams:reactive-streams:1.0.3
6 | org.slf4j:slf4j-api:1.7.30
7 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
8 | org.springframework:spring-beans:5.2.5.RELEASE
9 | org.springframework:spring-core:5.2.5.RELEASE
10 | org.springframework:spring-jcl:5.2.5.RELEASE
11 | org.springframework:spring-web:5.2.5.RELEASE
12 | org.springframework:spring-webflux:5.2.5.RELEASE
13 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/jacocoAgent.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/jacocoAnt.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 | org.jacoco:org.jacoco.ant:0.8.5
6 | org.jacoco:org.jacoco.core:0.8.5
7 | org.jacoco:org.jacoco.report:0.8.5
8 | org.ow2.asm:asm-analysis:7.2
9 | org.ow2.asm:asm-commons:7.2
10 | org.ow2.asm:asm-tree:7.2
11 | org.ow2.asm:asm:7.2
12 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/runtime.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/runtimeClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | io.projectreactor:reactor-core:3.3.4.RELEASE
5 | org.reactivestreams:reactive-streams:1.0.3
6 | org.slf4j:slf4j-api:1.7.30
7 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
8 | org.springframework:spring-beans:5.2.5.RELEASE
9 | org.springframework:spring-core:5.2.5.RELEASE
10 | org.springframework:spring-jcl:5.2.5.RELEASE
11 | org.springframework:spring-web:5.2.5.RELEASE
12 | org.springframework:spring-webflux:5.2.5.RELEASE
13 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/testAnnotationProcessor.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/testCompile.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/testCompileClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | io.projectreactor:reactor-core:3.3.4.RELEASE
5 | org.apiguardian:apiguardian-api:1.1.0
6 | org.junit.jupiter:junit-jupiter-api:5.5.2
7 | org.junit.platform:junit-platform-commons:1.5.2
8 | org.opentest4j:opentest4j:1.2.0
9 | org.reactivestreams:reactive-streams:1.0.3
10 | org.slf4j:slf4j-api:1.7.30
11 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
12 | org.springframework:spring-beans:5.2.5.RELEASE
13 | org.springframework:spring-core:5.2.5.RELEASE
14 | org.springframework:spring-jcl:5.2.5.RELEASE
15 | org.springframework:spring-web:5.2.5.RELEASE
16 | org.springframework:spring-webflux:5.2.5.RELEASE
17 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/testCompileOnly.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/testRuntime.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/api/gradle/dependency-locks/testRuntimeClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | ch.qos.logback:logback-classic:1.2.3
5 | ch.qos.logback:logback-core:1.2.3
6 | io.projectreactor:reactor-core:3.3.4.RELEASE
7 | org.apiguardian:apiguardian-api:1.1.0
8 | org.junit.jupiter:junit-jupiter-api:5.5.2
9 | org.junit.jupiter:junit-jupiter-engine:5.5.2
10 | org.junit.platform:junit-platform-commons:1.5.2
11 | org.junit.platform:junit-platform-engine:1.5.2
12 | org.opentest4j:opentest4j:1.2.0
13 | org.reactivestreams:reactive-streams:1.0.3
14 | org.slf4j:slf4j-api:1.7.30
15 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
16 | org.springframework:spring-beans:5.2.5.RELEASE
17 | org.springframework:spring-core:5.2.5.RELEASE
18 | org.springframework:spring-jcl:5.2.5.RELEASE
19 | org.springframework:spring-web:5.2.5.RELEASE
20 | org.springframework:spring-webflux:5.2.5.RELEASE
21 |
--------------------------------------------------------------------------------
/api/src/main/java/net/dv8tion/jda/api/audio/AudioSendHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2017 Austin Keener & Michael Ritter
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.dv8tion.jda.api.audio;
18 |
19 | import javax.sound.sampled.AudioFormat;
20 | import java.nio.ByteBuffer;
21 |
22 | /**
23 | * Interface used to send audio to Discord through JDA.
24 | */
25 | public interface AudioSendHandler
26 | {
27 | /**
28 | * Audio Input Format expected by JDA if {@link #isOpus()} returns false. 48KHz 16bit stereo signed BigEndian PCM.
29 | */
30 | AudioFormat INPUT_FORMAT = new AudioFormat(48000f, 16, 2, true, true);
31 |
32 | /**
33 | * If this method returns true JDA will attempt to retrieve audio data from this handler by calling
34 | * {@link #provide20MsAudio()}. The return value is checked each time JDA attempts send audio, so if
35 | * the developer wanted to start and stop sending audio it could be done by changing the value returned
36 | * by this method at runtime.
37 | *
38 | * @return If true, JDA will attempt to retrieve audio data from {@link #provide20MsAudio()}
39 | */
40 | boolean canProvide();
41 |
42 | /**
43 | * If {@link #canProvide()} returns true JDA will call this method in an attempt to retrieve audio data from the
44 | * handler. This method need to provide 20 Milliseconds of audio data as a byte array.
45 | *
46 | * Considering this system needs to be low-latency / high-speed, it is recommended that the loading of audio data
47 | * be done before hand or in parallel and not loaded from disk when this method is called by JDA. Attempting to load
48 | * all audio data from disk when this method is called will most likely cause issues due to IO blocking this thread.
49 | *
50 | * The provided audio data needs to be in the format: 48KHz 16bit stereo signed BigEndian PCM.
51 | *
Defined by: {@link net.dv8tion.jda.api.audio.AudioSendHandler#INPUT_FORMAT AudioSendHandler.INPUT_FORMAT}.
52 | *
If {@link #isOpus()} is set to return true, then it should be in pre-encoded Opus format instead.
53 | *
54 | * @return Should return a byte[] containing 20 Milliseconds of audio.
55 | */
56 | ByteBuffer provide20MsAudio();
57 |
58 | /**
59 | * If this method returns true JDA will treat the audio data provided by {@link #provide20MsAudio()} as a pre-encoded
60 | * 20 Millisecond packet of Opus audio. This means that JDA WILL NOT attempt to encode the audio as Opus, but
61 | * will provide it to Discord exactly as it is given.
62 | *
63 | * @return If true, JDA will not attempt to encode the provided audio data as Opus.
64 | *
Default - False.
65 | */
66 | default boolean isOpus()
67 | {
68 | return false;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/api/src/main/java/net/dv8tion/jda/api/audio/factory/IAudioSendFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2018 Austin Keener & Michael Ritter & Florian Spieß
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.dv8tion.jda.api.audio.factory;
18 |
19 | /**
20 | * Factory interface for the creation of new {@link net.dv8tion.jda.api.audio.factory.IAudioSendSystem IAudioSendSystem} objects.
21 | */
22 | public interface IAudioSendFactory
23 | {
24 | /**
25 | * Called by JDA's audio system when a new {@link net.dv8tion.jda.api.audio.factory.IAudioSendSystem IAudioSendSystem}
26 | * instance is needed to handle the sending of UDP audio packets to discord.
27 | *
28 | * @param packetProvider
29 | * The connection provided to the new {@link net.dv8tion.jda.api.audio.factory.IAudioSendSystem IAudioSendSystem}
30 | * object for proper setup and usage.
31 | *
32 | * @return The newly constructed IAudioSendSystem, ready for {@link IAudioSendSystem#start()} to be called.
33 | */
34 | IAudioSendSystem createSendSystem(IPacketProvider packetProvider);
35 | }
36 |
--------------------------------------------------------------------------------
/api/src/main/java/net/dv8tion/jda/api/audio/factory/IAudioSendSystem.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2018 Austin Keener & Michael Ritter & Florian Spieß
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.dv8tion.jda.api.audio.factory;
18 |
19 | /**
20 | * Interface that acts as a UDP audio packet sending loop.
21 | *
This interface is provided so that developers can provide their own implementation for different management
22 | * of thread pools, process usage, and even for forwarding to native binaries implemented in other languages like C
23 | * to avoid problems with JVM GC StopTheWorld events.
24 | */
25 | public interface IAudioSendSystem
26 | {
27 | /**
28 | * This represents the start of the loop, similar to {@link Thread#start()}, and after a call to this method JDA
29 | * assumes that the instance will be sending UDP audio packets in a loop.
30 | *
31 | * Note: The packet sending loop should NOT be started on the current thread. I.E: This method should not
32 | * block forever, in the same way that {@link Thread#start()} does not. Just like in Thread, the running action of
33 | * this system should be implemented asynchronously.
34 | */
35 | void start();
36 |
37 | /**
38 | * This represents the destruction of this instance and should be used to perform all necessary cleanup and shutdown
39 | * operations needed to free resources.
40 | *
41 | * Note: This method can be called at any time after instance creation ({@link #start()} may not yet have been called),
42 | * and it is possible that this method could be called more than once due to edge-case shutdown conditions.
43 | */
44 | void shutdown();
45 | }
46 |
--------------------------------------------------------------------------------
/api/src/main/java/net/dv8tion/jda/api/audio/factory/IPacketProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2018 Austin Keener & Michael Ritter & Florian Spieß
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.dv8tion.jda.api.audio.factory;
18 |
19 | import net.dv8tion.jda.api.audio.hooks.ConnectionStatus;
20 |
21 | import javax.annotation.concurrent.NotThreadSafe;
22 | import java.net.DatagramPacket;
23 | import java.net.DatagramSocket;
24 | import java.net.InetSocketAddress;
25 | import java.nio.ByteBuffer;
26 |
27 | /**
28 | * Represents the connection between a {@link net.dv8tion.jda.api.audio.factory.IAudioSendSystem IAudioSendSystem} and
29 | * JDA's internal audio system, providing access to audio packets built from data provided from
30 | * {@link net.dv8tion.jda.api.audio.AudioSendHandler AudioSendHandlers}.
31 | *
32 | *
Note that this provider is not thread-safe!
33 | */
34 | @NotThreadSafe
35 | public interface IPacketProvider
36 | {
37 | /**
38 | * Provides a unique String identifier for the connection.
39 | *
Uses shard information and specific audio connection information to build string.
40 | *
41 | *
THIS IS NOT AVAILABLE IN MAGMA
42 | *
43 | * @return Never-null String unique to this audio connection.
44 | */
45 | String getIdentifier();
46 |
47 | /**
48 | * Provides the current channel that this connection is transmitting to.
49 | *
50 | *
THIS IS NOT AVAILABLE IN MAGMA
51 | *
52 | * @return The id of the connected channel that this connection is sending to.
53 | */
54 | String getConnectedChannel();
55 |
56 | /**
57 | * The UDP connection for this audio connection. The Send System
58 | * uses this socket to send audio packets to discord, and this is also the socket used to receive audio packets from discord.
59 | *
If you are implementing your own system, it is recommended that you used this connection as it is part of JDA's internal
60 | * system that JDA monitors for errors and closures. It should be noted however that using this is not required to
61 | * send audio packets if the developer wishes to open their own UDP socket to send from.
62 | *
63 | * @return The UDP socket connection used for audio sending.
64 | */
65 | DatagramSocket getUdpSocket();
66 |
67 | /**
68 | * The connected socket address for this audio connection. This can be useful for developers
69 | * to open their own socket for datagram sending and allows to avoid using {@link #getNextPacket(boolean)}.
70 | *
71 | * @return {@link InetSocketAddress} of the current UDP connection
72 | */
73 | InetSocketAddress getSocketAddress();
74 |
75 | /**
76 | * Used to retrieve an audio packet to send to Discord. The packet provided is already converted to Opus and
77 | * encrypted, and as such is completely ready to be sent to Discord. The {@code changeTalking} parameter is used
78 | * to control whether or not the talking indicator should be changed if the
79 | * {@link net.dv8tion.jda.api.audio.AudioSendHandler AudioSendHandler} cannot provide an audio packet.
80 | *
81 | *
Use case for this parameter would be front-loading or queuing many audio packets ahead of send time, and if the AudioSendHandler
82 | * did not have enough to fill the entire queue, you would have {@code changeTalking} set to {@code false} until the queue
83 | * was empty. At that point, you would switch to {@code true} when requesting a new packet due to the fact that if
84 | * one was not available, the developer would not have a packet to send, thus the logged in account is no longer "talking".
85 | *
86 | *
Note: When the AudioSendHandler cannot or does not provide a new packet to send, this method will return null.
87 | *
88 | *
The buffer used here may be used again on the next call to this getter, if you plan on storing the data copy it.
89 | * The buffer was created using {@link ByteBuffer#allocate(int)} and is not direct.
90 | *
91 | * @param changeTalking
92 | * Whether or not to change the talking indicator if the AudioSendHandler cannot provide a new audio packet.
93 | *
94 | * @return Possibly-null {@link ByteBuffer} containing an encoded and encrypted packet
95 | * of audio data ready to be sent to discord.
96 | */
97 | ByteBuffer getNextPacketRaw(boolean changeTalking);
98 |
99 | /**
100 | * Used to retrieve an audio packet to send to Discord. The packet provided is already converted to Opus and
101 | * encrypted, and as such is completely ready to be sent to Discord. The {@code changeTalking} parameter is used
102 | * to control whether or not the talking indicator should be changed if the
103 | * {@link net.dv8tion.jda.api.audio.AudioSendHandler AudioSendHandler} cannot provide an audio packet.
104 | *
105 | *
Use case for this parameter would be front-loading or queuing many audio packets ahead of send time, and if the AudioSendHandler
106 | * did not have enough to fill the entire queue, you would have {@code changeTalking} set to {@code false} until the queue
107 | * was empty. At that point, you would switch to {@code true} when requesting a new packet due to the fact that if
108 | * one was not available, the developer would not have a packet to send, thus the logged in account is no longer "talking".
109 | *
110 | *
Note: When the AudioSendHandler cannot or does not provide a new packet to send, this method will return null.
111 | *
112 | * @param changeTalking
113 | * Whether or not to change the talking indicator if the AudioSendHandler cannot provide a new audio packet.
114 | *
115 | * @return Possibly-null {@link java.net.DatagramPacket DatagramPacket} containing an encoded and encrypted packet
116 | * of audio data ready to be sent to discord.
117 | */
118 | DatagramPacket getNextPacket(boolean changeTalking);
119 |
120 | /**
121 | * This method is used to indicate a connection error to JDA so that the connection can be properly shutdown.
122 | *
This is useful if, during setup or operation, an unrecoverable error is encountered.
123 | *
124 | * @param status
125 | * The {@link net.dv8tion.jda.api.audio.hooks.ConnectionStatus ConnectionStatus} being reported to JDA
126 | * indicating an error with connection.
127 | */
128 | void onConnectionError(ConnectionStatus status);
129 |
130 | /**
131 | * This method is used to indicate to JDA that the UDP connection has been lost, whether that be due internet loss
132 | * or some other unknown reason. This is similar to
133 | * {@link #onConnectionError(net.dv8tion.jda.api.audio.hooks.ConnectionStatus)} as it provides a default error
134 | * reason of {@link net.dv8tion.jda.api.audio.hooks.ConnectionStatus#ERROR_LOST_CONNECTION}.
135 | */
136 | void onConnectionLost();
137 | }
138 |
--------------------------------------------------------------------------------
/api/src/main/java/net/dv8tion/jda/api/audio/hooks/ConnectionStatus.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2017 Austin Keener & Michael Ritter
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.dv8tion.jda.api.audio.hooks;
18 |
19 | /**
20 | * Represents the connection status of an audio connection.
21 | *
22 | * NOTE: This class is only present in Magma due to {@link net.dv8tion.jda.api.audio.factory.IPacketProvider IPacketProviders}
23 | * dependency on it
24 | */
25 | public enum ConnectionStatus
26 | {
27 | /** Indicates that there is no open connection or that the connection was closed by choice, not by error.*/
28 | NOT_CONNECTED,
29 | /** JDA is waiting on Discord to send a valid endpoint which to connect the audio websocket to.*/
30 | CONNECTING_AWAITING_ENDPOINT,
31 | /** JDA has received a valid endpoint and is attempting to setup and connect the audio websocket */
32 | CONNECTING_AWAITING_WEBSOCKET_CONNECT,
33 | /** JDA has connected the audio websocket to Discord and has sent the authentication information, awaiting reply.*/
34 | CONNECTING_AWAITING_AUTHENTICATING,
35 | /**
36 | * JDA successfully authenticated the audio websocket and it now attempting UDP discovery. UDP discovery involves
37 | * opening a UDP socket and sending a packet to a provided Discord remote resource which responds with the
38 | * external ip and port which the packet was sent from.
39 | */
40 | CONNECTING_ATTEMPTING_UDP_DISCOVERY,
41 | /**
42 | * After determining our external ip and port, JDA forwards this information to Discord so that it can send
43 | * audio packets for us to properly receive. At this point, JDA is waiting for final websocket READY.
44 | */
45 | CONNECTING_AWAITING_READY,
46 | /** The audio connection has been successfully setup and is ready for use. */
47 | CONNECTED,
48 | /**
49 | * Indicates that the channel which the audio connection was connected to was deleted, thus the connection was severed.
50 | *
This is a non-reconnectable error.
51 | * */
52 | DISCONNECTED_CHANNEL_DELETED,
53 |
54 | //All will attempt to reconnect unless autoReconnect is disabled
55 | /**
56 | * Indicates that the connection was lost, either via UDP socket problems or the audio Websocket disconnecting.
57 | *
This is typically caused by a brief loss of internet which results in connection loss.
58 | *
JDA automatically attempts to reconnect when this error occurs.
59 | */
60 | ERROR_LOST_CONNECTION,
61 | /**
62 | * Indicates that the audio Websocket was unable to connect to discord. This could be due to an internet
63 | * problem causing a connection problem or an error on Discord's side (possibly due to load)
64 | *
JDA automatically attempts to reconnect when this error occurs.
65 | */
66 | ERROR_WEBSOCKET_UNABLE_TO_CONNECT,
67 | /**
68 | * Indicates that the UDP setup failed. This is caused when JDA cannot properly communicate with Discord to
69 | * discover the system's external IP and port which audio data will be sent from. Typically caused by an internet
70 | * problem or an overly aggressive NAT port table.
71 | *
JDA automatically attempts to reconnect when this error occurs.
72 | */
73 | ERROR_UDP_UNABLE_TO_CONNECT,
74 | ERROR_CANNOT_RESUME, DISCONNECTED_AUTHENTICATION_FAILURE,
75 | }
76 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/MagmaApi.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | import edu.umd.cs.findbugs.annotations.Nullable;
20 | import net.dv8tion.jda.api.audio.AudioSendHandler;
21 | import reactor.core.publisher.Flux;
22 | import space.npstr.magma.api.event.MagmaEvent;
23 |
24 | import java.net.DatagramSocket;
25 | import java.util.List;
26 | import java.util.Set;
27 |
28 | /**
29 | * Created by napster on 24.04.18.
30 | *
31 | * Public API. These methods may be called by users of Magma.
32 | */
33 | @SuppressWarnings("unused")
34 | public interface MagmaApi {
35 |
36 | /**
37 | * The UDP client used to NAT hole punch.
38 | *
This is closed by {@link #shutdown()}.
39 | *
40 | * @return The DatagramSocket
41 | */
42 | DatagramSocket getDatagramSocket();
43 |
44 | /**
45 | * Release all resources held.
46 | */
47 | void shutdown();
48 |
49 | /**
50 | * @return a Reactor stream that can be subscribed to for event handling
51 | */
52 | Flux getEventStream();
53 |
54 | /**
55 | * Also see: https://discordapp.com/developers/docs/topics/voice-connections#retrieving-voice-server-information-example-voice-server-update-payload
56 | *
57 | * @param member
58 | * Id of the bot account that this update belongs to composed with the id of the guild whose voice server
59 | * shall be updated. The user id is something your use code should keep track of, the guild id can be
60 | * extracted from the op 0 VOICE_SERVER_UPDATE event that should be triggering a call to this method in the
61 | * first place.
62 | * @param serverUpdate
63 | * A composite of session id, endpoint and token. Most of that information can be extracted from the op 0
64 | * VOICE_SERVER_UPDATE event that should be triggering a call to this method in the first place.
65 | *
66 | * @see Member
67 | * @see ServerUpdate
68 | */
69 | void provideVoiceServerUpdate(final Member member, final ServerUpdate serverUpdate);
70 |
71 | /**
72 | * Set the {@link AudioSendHandler} for a bot member.
73 | *
74 | * @param member
75 | * user id + guild id of the bot member for which the send handler shall be set
76 | * @param sendHandler
77 | * The send handler to be set. You need to implement this yourself. This is a JDA interface so if you have
78 | * written voice code with JDA before you reuse your existing code.
79 | *
80 | * @see Member
81 | */
82 | void setSendHandler(final Member member, final AudioSendHandler sendHandler);
83 |
84 | /**
85 | * The {@link SpeakingMode SpeakingMode} to use.
86 | * @param member
87 | * user id + guild id of the bot member for which the speaking mode shall be set
88 | * @param mode
89 | * EnumSet containing the speaking modes to apply
90 | * @see Member
91 | */
92 | void setSpeakingMode(final Member member, @Nullable final Set mode);
93 |
94 | /**
95 | * Remove the {@link AudioSendHandler} for a bot member.
96 | *
97 | * @param member
98 | * user id + guild id of the bot member for which the send handler shall be removed
99 | *
100 | * @see Member
101 | */
102 | void removeSendHandler(final Member member);
103 |
104 | /**
105 | * Close the audio connection for a bot member.
106 | *
107 | * @param member
108 | * user id + guild id of the bot member for which the audio connection shall be closed
109 | *
110 | * @see Member
111 | */
112 | void closeConnection(final Member member);
113 |
114 |
115 | /**
116 | * @return a list of all {@link WebsocketConnectionState WebsocketConnectionStates} detailing the state of
117 | * the audio stacks managed by this {@link MagmaApi} instance
118 | */
119 | List getAudioConnectionStates();
120 | }
121 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/MdcKey.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | /**
20 | * Created by napster on 17.07.18.
21 | */
22 | public class MdcKey {
23 |
24 | public static final String GUILD = "mmGuild";
25 | public static final String BOT = "mmBot";
26 |
27 | private MdcKey() {}
28 | }
29 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/Member.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | import org.immutables.value.Value;
20 |
21 | /**
22 | * Created by napster on 23.05.18.
23 | *
24 | * Discord supports one single audio connection per user and guild (also called a "member").
25 | * This means, an audio connection is exactly identified by those two datapoints,
26 | * and all of the methods of Magma require those to correctly identify the connection
27 | * that you want to open/close/change something about.
28 | *
29 | * This is the class that composes the user id and guild id to identify such a member.
30 | *
31 | * Build one of these by using the autogenerated builder like so:
32 | *
33 | * {@code
34 | * Member someMember = MagmaMember.builder()
35 | * .userId("...")
36 | * .guildId("...")
37 | * .build();
38 | * }
39 | *
40 | */
41 | @Value.Immutable
42 | @Value.Style(
43 | typeAbstract = "*",
44 | typeImmutable = "Magma*"
45 | )
46 | public abstract class Member {
47 |
48 | /**
49 | * Discord snowflake detailing the user of this member
50 | */
51 | public abstract String getUserId();
52 |
53 | /**
54 | * Discord snowflake detailing the guild of this member
55 | */
56 | public abstract String getGuildId();
57 |
58 |
59 | @Value.Check
60 | protected void stringsNotEmpty() {
61 | final String userId = this.getUserId();
62 | if (userId.isEmpty()) {
63 | throw new IllegalArgumentException("Provided user id is empty!");
64 | }
65 | try {
66 | //noinspection ResultOfMethodCallIgnored
67 | Long.parseUnsignedLong(userId);
68 | } catch (final NumberFormatException e) {
69 | throw new IllegalArgumentException("Provided user id '" + userId + "' is not a valid discord snowflake.");
70 | }
71 |
72 |
73 | final String guildId = this.getGuildId();
74 | if (guildId.isEmpty()) {
75 | throw new IllegalArgumentException("Provided guild id is empty!");
76 | }
77 |
78 | try {
79 | //noinspection ResultOfMethodCallIgnored
80 | Long.parseUnsignedLong(guildId);
81 | } catch (final NumberFormatException e) {
82 | throw new IllegalArgumentException("Provided guild id '" + guildId + "' is not a valid discord snowflake.");
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/ServerUpdate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | import org.immutables.value.Value;
20 |
21 | /**
22 | * Created by napster on 23.05.18.
23 | *
24 | * Build one of these by using the autogenerated builder like so:
25 | *
26 | * {@code
27 | * ServerUpdate serverUpdate = MagmaServerUpdate.builder()
28 | * .sessionId("...")
29 | * .endpoint("...")
30 | * .token("...")
31 | * .build();
32 | * }
33 | *
34 | */
35 | @Value.Immutable
36 | @Value.Style(
37 | typeAbstract = "*",
38 | typeImmutable = "Magma*"
39 | )
40 | public abstract class ServerUpdate {
41 |
42 | /**
43 | * The session id of the voice state of the member to which this server update belongs.
44 | */
45 | public abstract String getSessionId();
46 |
47 | /**
48 | * The endpoint to connect to. If the event you received from Discord has no endpoint, you can safely
49 | * discard it, until you received one with a valid endpoint. Can be extracted from the op 0
50 | * VOICE_SERVER_UPDATE event.
51 | */
52 | public abstract String getEndpoint();
53 |
54 | /**
55 | * Token that can be extracted from the op 0 VOICE_SERVER_UPDATE event.
56 | */
57 | public abstract String getToken();
58 |
59 |
60 | @Value.Check
61 | protected void stringsNotEmpty() {
62 | if (this.getSessionId().isEmpty()) {
63 | throw new IllegalArgumentException("Provided session id is empty!");
64 | }
65 | if (this.getEndpoint().isEmpty()) {
66 | throw new IllegalArgumentException("Provided endpoint is empty!");
67 | }
68 | if (this.getToken().isEmpty()) {
69 | throw new IllegalArgumentException("Provided token is empty!");
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/SpeakingMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2019 Florian Spieß
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | import edu.umd.cs.findbugs.annotations.Nullable;
20 |
21 | import java.util.Set;
22 |
23 | public enum SpeakingMode {
24 | VOICE(1),
25 | SOUNDSHARE(2),
26 | PRIORITY(4);
27 |
28 | private final int key;
29 |
30 | SpeakingMode(int key) {
31 | this.key = key;
32 | }
33 |
34 | public int getKey() {
35 | return key;
36 | }
37 |
38 | public static int toMask(@Nullable Set mode) {
39 | if (mode == null || mode.isEmpty()) {
40 | return 0;
41 | }
42 | int mask = 0;
43 | for (SpeakingMode m : mode) {
44 | mask |= m.getKey();
45 | }
46 | return mask;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/WebsocketConnectionState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | import org.immutables.value.Value;
20 |
21 | /**
22 | * Created by napster on 25.10.18.
23 | */
24 | @Value.Immutable
25 | @Value.Style(
26 | typeAbstract = "*",
27 | typeImmutable = "Magma*"
28 | )
29 | public abstract class WebsocketConnectionState {
30 |
31 | /**
32 | * @return user and guild coordinates of this audio connection
33 | */
34 | public abstract Member getMember();
35 |
36 | /**
37 | * @return phase the websocket connection of this member finds itself in, see {@link Phase}
38 | */
39 | public abstract Phase getPhase();
40 |
41 |
42 | public enum Phase {
43 |
44 | /**
45 | * The connection is connecting initially.
46 | */
47 | CONNECTING,
48 |
49 | /**
50 | * We have received a Ready or Resume payload and have not disconnected yet.
51 | */
52 | CONNECTED,
53 |
54 | /**
55 | * Websocket connection has been closed, either by Discord, due to internet issues, or by ourselves.
56 | */
57 | DISCONNECTED,
58 |
59 | /**
60 | * We are attempting to resume the connection.
61 | */
62 | RESUMING,
63 |
64 | /**
65 | * No websocket connection is present, even though there is an audio stack present for this member.
66 | */
67 | NO_CONNECTION,
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/event/ImmutableApiEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api.event;
18 |
19 | import org.immutables.value.Value;
20 |
21 | @Value.Style(
22 | typeImmutable = "*ApiEvent",
23 | stagedBuilder = true
24 | )
25 | public @interface ImmutableApiEvent {}
26 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/event/MagmaEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api.event;
18 |
19 | /**
20 | * Base interface of API events
21 | */
22 | public interface MagmaEvent {}
23 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/event/WebSocketClosed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api.event;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.api.Member;
21 |
22 | /**
23 | * This event is fired when an audio web socket is closed. However, this event is not fired if we are trying to
24 | * resume (i.e reconnect) automatically unless resuming causes a new socket to close.
25 | */
26 | @SuppressWarnings("unused")
27 | @Value.Immutable
28 | @ImmutableApiEvent
29 | public abstract class WebSocketClosed implements MagmaEvent {
30 |
31 | public abstract Member getMember();
32 |
33 | public abstract int getCloseCode();
34 |
35 | public abstract String getReason();
36 |
37 | public abstract boolean isByRemote();
38 | }
39 |
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/event/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.api.event;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/api/src/main/java/space/npstr/magma/api/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.api;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/api/src/test/java/space/npstr/magma/api/MemberTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | import org.junit.jupiter.api.Test;
20 | import org.junit.jupiter.api.function.Executable;
21 |
22 | import static org.junit.jupiter.api.Assertions.assertEquals;
23 | import static org.junit.jupiter.api.Assertions.assertThrows;
24 |
25 | /**
26 | * Created by napster on 23.05.18.
27 | */
28 | public class MemberTest {
29 |
30 | @Test
31 | public void emptyUserId() {
32 | final Executable ex = () -> MagmaMember.builder()
33 | .userId("")
34 | .guildId("174820236481134592")
35 | .build();
36 |
37 | assertThrows(IllegalArgumentException.class, ex, "Accepted empty user id");
38 | }
39 |
40 | @Test
41 | public void emptyGuildId() {
42 | final Executable ex = () -> MagmaMember.builder()
43 | .userId("166604053629894657")
44 | .guildId("")
45 | .build();
46 |
47 | assertThrows(IllegalArgumentException.class, ex, "Accepted empty guild id");
48 | }
49 |
50 |
51 | @Test
52 | public void userIdNotASnowflake() {
53 | final Executable ex = () -> MagmaMember.builder()
54 | .userId("this is not a valid snowflake")
55 | .guildId("174820236481134592")
56 | .build();
57 |
58 | assertThrows(IllegalArgumentException.class, ex, "Accepted obviously invalid snowflake as a user id");
59 | }
60 |
61 |
62 | @Test
63 | public void guildIdNotASnowflake() {
64 | final Executable ex = () -> MagmaMember.builder()
65 | .userId("166604053629894657")
66 | .guildId("totally valid snowflake kappa")
67 | .build();
68 |
69 | assertThrows(IllegalArgumentException.class, ex, "Accepted obviously invalid snowflake as a guild id");
70 | }
71 |
72 | @Test
73 | public void valid() {
74 | final String userId = "166604053629894657";
75 | final String guildId = "174820236481134592";
76 | final Member member = MagmaMember.builder()
77 | .userId(userId)
78 | .guildId(guildId)
79 | .build();
80 |
81 | assertEquals(userId, member.getUserId(), "User id modified by builder");
82 | assertEquals(guildId, member.getGuildId(), "Guild id modified by builder");
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/api/src/test/java/space/npstr/magma/api/ServerUpdateTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.api;
18 |
19 | import org.junit.jupiter.api.Test;
20 | import org.junit.jupiter.api.function.Executable;
21 |
22 | import static org.junit.jupiter.api.Assertions.assertEquals;
23 | import static org.junit.jupiter.api.Assertions.assertThrows;
24 |
25 | /**
26 | * Created by napster on 23.05.18.
27 | */
28 | public class ServerUpdateTest {
29 |
30 | @Test
31 | public void emptySessionId() {
32 | final Executable ex = () -> MagmaServerUpdate.builder()
33 | .sessionId("")
34 | .endpoint("127.0.0.1")
35 | .token("top_secret")
36 | .build();
37 |
38 | assertThrows(IllegalArgumentException.class, ex, "Accepted empty session id");
39 | }
40 |
41 | @Test
42 | public void emptyEndpoint() {
43 | final Executable ex = () -> MagmaServerUpdate.builder()
44 | .sessionId("blargh")
45 | .endpoint("")
46 | .token("top_secret")
47 | .build();
48 |
49 | assertThrows(IllegalArgumentException.class, ex, "Accepted empty endpoint");
50 | }
51 |
52 | @Test
53 | public void emptyToken() {
54 | final Executable ex = () -> MagmaServerUpdate.builder()
55 | .sessionId("blargh")
56 | .endpoint("127.0.0.1")
57 | .token("")
58 | .build();
59 |
60 | assertThrows(IllegalArgumentException.class, ex, "Accepted empty token");
61 | }
62 |
63 | @Test
64 | public void valid() {
65 | final String sessionId = "blargh";
66 | final String endpoint = "127.0.0.1";
67 | final String token = "top_secret";
68 | final ServerUpdate serverUpdate = MagmaServerUpdate.builder()
69 | .sessionId(sessionId)
70 | .endpoint(endpoint)
71 | .token(token)
72 | .build();
73 |
74 | assertEquals(sessionId, serverUpdate.getSessionId(), "Session id modified by builder");
75 | assertEquals(endpoint, serverUpdate.getEndpoint(), "Endpoint modified by builder");
76 | assertEquals(token, serverUpdate.getToken(), "Token modified by builder");
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/annotationProcessor.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.immutables:value:2.8.3
5 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
6 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/archives.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/buildscript-classpath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | backport-util-concurrent:backport-util-concurrent:3.1
5 | classworlds:classworlds:1.1-alpha-2
6 | com.github.ben-manes:gradle-versions-plugin:0.28.0
7 | com.googlecode.javaewah:JavaEWAH:1.1.6
8 | com.jcraft:jsch:0.1.55
9 | com.jcraft:jzlib:1.1.1
10 | com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4
11 | com.thoughtworks.xstream:xstream:1.4.10
12 | commons-beanutils:commons-beanutils:1.8.0
13 | commons-codec:commons-codec:1.6
14 | commons-collections:commons-collections:3.2.1
15 | commons-lang:commons-lang:2.4
16 | commons-logging:commons-logging:1.1.1
17 | junit:junit:3.8.1
18 | nekohtml:nekohtml:1.9.6.2
19 | nekohtml:xercesMinimal:1.9.6.2
20 | net.sf.ezmorph:ezmorph:1.0.6
21 | net.sf.json-lib:json-lib:2.3
22 | net.sourceforge.nekohtml:nekohtml:1.9.16
23 | org.ajoberstar.grgit:grgit-core:4.0.1
24 | org.ajoberstar.grgit:grgit-gradle:4.0.1
25 | org.apache.ant:ant-launcher:1.8.0
26 | org.apache.ant:ant:1.8.0
27 | org.apache.httpcomponents:httpclient:4.2.1
28 | org.apache.httpcomponents:httpcore:4.2.1
29 | org.apache.maven.wagon:wagon-file:1.0-beta-6
30 | org.apache.maven.wagon:wagon-http-lightweight:1.0-beta-6
31 | org.apache.maven.wagon:wagon-http-shared:1.0-beta-6
32 | org.apache.maven.wagon:wagon-provider-api:1.0-beta-6
33 | org.apache.maven:maven-ant-tasks:2.1.3
34 | org.apache.maven:maven-artifact-manager:2.2.1
35 | org.apache.maven:maven-artifact:2.2.1
36 | org.apache.maven:maven-error-diagnostics:2.2.1
37 | org.apache.maven:maven-model:2.2.1
38 | org.apache.maven:maven-plugin-registry:2.2.1
39 | org.apache.maven:maven-profile:2.2.1
40 | org.apache.maven:maven-project:2.2.1
41 | org.apache.maven:maven-repository-metadata:2.2.1
42 | org.apache.maven:maven-settings:2.2.1
43 | org.bouncycastle:bcpg-jdk15on:1.64
44 | org.bouncycastle:bcpkix-jdk15on:1.64
45 | org.bouncycastle:bcprov-jdk15on:1.64
46 | org.codehaus.groovy.modules.http-builder:http-builder:0.7.2
47 | org.codehaus.plexus:plexus-container-default:1.0-alpha-9-stable-1
48 | org.codehaus.plexus:plexus-interpolation:1.11
49 | org.codehaus.plexus:plexus-utils:1.5.15
50 | org.eclipse.jgit:org.eclipse.jgit:5.6.0.201912101111-r
51 | org.slf4j:slf4j-api:1.7.2
52 | org.sonarsource.scanner.api:sonar-scanner-api:2.14.0.2002
53 | org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8.0.1969
54 | xerces:xercesImpl:2.9.1
55 | xml-resolver:xml-resolver:1.2
56 | xmlpull:xmlpull:1.1.3.1
57 | xpp3:xpp3_min:1.1.4c
58 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/compile.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/compileClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.github.spotbugs:spotbugs-annotations:4.0.1
5 | com.google.code.findbugs:jsr305:3.0.2
6 | io.projectreactor:reactor-core:3.3.4.RELEASE
7 | org.immutables:value:2.8.3
8 | org.reactivestreams:reactive-streams:1.0.3
9 | org.slf4j:slf4j-api:1.7.30
10 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
11 | org.springframework:spring-beans:5.2.5.RELEASE
12 | org.springframework:spring-core:5.2.5.RELEASE
13 | org.springframework:spring-jcl:5.2.5.RELEASE
14 | org.springframework:spring-web:5.2.5.RELEASE
15 | org.springframework:spring-webflux:5.2.5.RELEASE
16 | space.npstr:annotations:0.0.2
17 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/compileOnly.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.github.spotbugs:spotbugs-annotations:4.0.1
5 | com.google.code.findbugs:jsr305:3.0.2
6 | org.immutables:value:2.8.3
7 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
8 | space.npstr:annotations:0.0.2
9 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/default.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.jcabi:jcabi-log:0.14
5 | com.jcabi:jcabi-manifests:1.1
6 | io.netty:netty-buffer:4.1.48.Final
7 | io.netty:netty-codec-http2:4.1.48.Final
8 | io.netty:netty-codec-http:4.1.48.Final
9 | io.netty:netty-codec-socks:4.1.48.Final
10 | io.netty:netty-codec:4.1.48.Final
11 | io.netty:netty-common:4.1.48.Final
12 | io.netty:netty-handler-proxy:4.1.48.Final
13 | io.netty:netty-handler:4.1.48.Final
14 | io.netty:netty-resolver:4.1.48.Final
15 | io.netty:netty-transport-native-epoll:4.1.48.Final
16 | io.netty:netty-transport-native-unix-common:4.1.48.Final
17 | io.netty:netty-transport:4.1.48.Final
18 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
19 | io.projectreactor:reactor-core:3.3.4.RELEASE
20 | org.json:json:20190722
21 | org.reactivestreams:reactive-streams:1.0.3
22 | org.slf4j:slf4j-api:1.7.30
23 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
24 | org.springframework:spring-beans:5.2.5.RELEASE
25 | org.springframework:spring-core:5.2.5.RELEASE
26 | org.springframework:spring-jcl:5.2.5.RELEASE
27 | org.springframework:spring-web:5.2.5.RELEASE
28 | org.springframework:spring-webflux:5.2.5.RELEASE
29 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/jacocoAgent.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/jacocoAnt.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 | org.jacoco:org.jacoco.ant:0.8.5
6 | org.jacoco:org.jacoco.core:0.8.5
7 | org.jacoco:org.jacoco.report:0.8.5
8 | org.ow2.asm:asm-analysis:7.2
9 | org.ow2.asm:asm-commons:7.2
10 | org.ow2.asm:asm-tree:7.2
11 | org.ow2.asm:asm:7.2
12 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/runtime.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/runtimeClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.jcabi:jcabi-log:0.14
5 | com.jcabi:jcabi-manifests:1.1
6 | io.netty:netty-buffer:4.1.48.Final
7 | io.netty:netty-codec-http2:4.1.48.Final
8 | io.netty:netty-codec-http:4.1.48.Final
9 | io.netty:netty-codec-socks:4.1.48.Final
10 | io.netty:netty-codec:4.1.48.Final
11 | io.netty:netty-common:4.1.48.Final
12 | io.netty:netty-handler-proxy:4.1.48.Final
13 | io.netty:netty-handler:4.1.48.Final
14 | io.netty:netty-resolver:4.1.48.Final
15 | io.netty:netty-transport-native-epoll:4.1.48.Final
16 | io.netty:netty-transport-native-unix-common:4.1.48.Final
17 | io.netty:netty-transport:4.1.48.Final
18 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
19 | io.projectreactor:reactor-core:3.3.4.RELEASE
20 | org.json:json:20190722
21 | org.reactivestreams:reactive-streams:1.0.3
22 | org.slf4j:slf4j-api:1.7.30
23 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
24 | org.springframework:spring-beans:5.2.5.RELEASE
25 | org.springframework:spring-core:5.2.5.RELEASE
26 | org.springframework:spring-jcl:5.2.5.RELEASE
27 | org.springframework:spring-web:5.2.5.RELEASE
28 | org.springframework:spring-webflux:5.2.5.RELEASE
29 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/testAnnotationProcessor.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/testCompile.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/testCompileClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | io.projectreactor:reactor-core:3.3.4.RELEASE
5 | org.apiguardian:apiguardian-api:1.1.0
6 | org.junit.jupiter:junit-jupiter-api:5.5.2
7 | org.junit.platform:junit-platform-commons:1.5.2
8 | org.opentest4j:opentest4j:1.2.0
9 | org.reactivestreams:reactive-streams:1.0.3
10 | org.slf4j:slf4j-api:1.7.30
11 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
12 | org.springframework:spring-beans:5.2.5.RELEASE
13 | org.springframework:spring-core:5.2.5.RELEASE
14 | org.springframework:spring-jcl:5.2.5.RELEASE
15 | org.springframework:spring-web:5.2.5.RELEASE
16 | org.springframework:spring-webflux:5.2.5.RELEASE
17 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/testCompileOnly.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/testRuntime.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/gradle/dependency-locks/testRuntimeClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | ch.qos.logback:logback-classic:1.2.3
5 | ch.qos.logback:logback-core:1.2.3
6 | com.jcabi:jcabi-log:0.14
7 | com.jcabi:jcabi-manifests:1.1
8 | io.netty:netty-buffer:4.1.48.Final
9 | io.netty:netty-codec-http2:4.1.48.Final
10 | io.netty:netty-codec-http:4.1.48.Final
11 | io.netty:netty-codec-socks:4.1.48.Final
12 | io.netty:netty-codec:4.1.48.Final
13 | io.netty:netty-common:4.1.48.Final
14 | io.netty:netty-handler-proxy:4.1.48.Final
15 | io.netty:netty-handler:4.1.48.Final
16 | io.netty:netty-resolver:4.1.48.Final
17 | io.netty:netty-transport-native-epoll:4.1.48.Final
18 | io.netty:netty-transport-native-unix-common:4.1.48.Final
19 | io.netty:netty-transport:4.1.48.Final
20 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
21 | io.projectreactor:reactor-core:3.3.4.RELEASE
22 | org.apiguardian:apiguardian-api:1.1.0
23 | org.json:json:20190722
24 | org.junit.jupiter:junit-jupiter-api:5.5.2
25 | org.junit.jupiter:junit-jupiter-engine:5.5.2
26 | org.junit.platform:junit-platform-commons:1.5.2
27 | org.junit.platform:junit-platform-engine:1.5.2
28 | org.opentest4j:opentest4j:1.2.0
29 | org.reactivestreams:reactive-streams:1.0.3
30 | org.slf4j:slf4j-api:1.7.30
31 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
32 | org.springframework:spring-beans:5.2.5.RELEASE
33 | org.springframework:spring-core:5.2.5.RELEASE
34 | org.springframework:spring-jcl:5.2.5.RELEASE
35 | org.springframework:spring-web:5.2.5.RELEASE
36 | org.springframework:spring-webflux:5.2.5.RELEASE
37 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schnapster/Magma/4f14645542d6d89969b39a4c07318ac6195e9119/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/impl/.gitignore:
--------------------------------------------------------------------------------
1 | build/
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/annotationProcessor.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.immutables:value:2.8.3
5 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
6 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/archives.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/compile.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/compileClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.github.spotbugs:spotbugs-annotations:4.0.1
5 | com.google.code.findbugs:jsr305:3.0.2
6 | com.jcabi:jcabi-log:0.14
7 | com.jcabi:jcabi-manifests:1.1
8 | io.netty:netty-buffer:4.1.48.Final
9 | io.netty:netty-codec-http2:4.1.48.Final
10 | io.netty:netty-codec-http:4.1.48.Final
11 | io.netty:netty-codec-socks:4.1.48.Final
12 | io.netty:netty-codec:4.1.48.Final
13 | io.netty:netty-common:4.1.48.Final
14 | io.netty:netty-handler-proxy:4.1.48.Final
15 | io.netty:netty-handler:4.1.48.Final
16 | io.netty:netty-resolver:4.1.48.Final
17 | io.netty:netty-transport-native-epoll:4.1.48.Final
18 | io.netty:netty-transport-native-unix-common:4.1.48.Final
19 | io.netty:netty-transport:4.1.48.Final
20 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
21 | io.projectreactor:reactor-core:3.3.4.RELEASE
22 | org.immutables:value:2.8.3
23 | org.json:json:20190722
24 | org.reactivestreams:reactive-streams:1.0.3
25 | org.slf4j:slf4j-api:1.7.30
26 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
27 | org.springframework:spring-beans:5.2.5.RELEASE
28 | org.springframework:spring-core:5.2.5.RELEASE
29 | org.springframework:spring-jcl:5.2.5.RELEASE
30 | org.springframework:spring-web:5.2.5.RELEASE
31 | org.springframework:spring-webflux:5.2.5.RELEASE
32 | space.npstr:annotations:0.0.2
33 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/compileOnly.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.github.spotbugs:spotbugs-annotations:4.0.1
5 | com.google.code.findbugs:jsr305:3.0.2
6 | org.immutables:value:2.8.3
7 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
8 | space.npstr:annotations:0.0.2
9 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/default.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.jcabi:jcabi-log:0.14
5 | com.jcabi:jcabi-manifests:1.1
6 | io.netty:netty-buffer:4.1.48.Final
7 | io.netty:netty-codec-http2:4.1.48.Final
8 | io.netty:netty-codec-http:4.1.48.Final
9 | io.netty:netty-codec-socks:4.1.48.Final
10 | io.netty:netty-codec:4.1.48.Final
11 | io.netty:netty-common:4.1.48.Final
12 | io.netty:netty-handler-proxy:4.1.48.Final
13 | io.netty:netty-handler:4.1.48.Final
14 | io.netty:netty-resolver:4.1.48.Final
15 | io.netty:netty-transport-native-epoll:4.1.48.Final
16 | io.netty:netty-transport-native-unix-common:4.1.48.Final
17 | io.netty:netty-transport:4.1.48.Final
18 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
19 | io.projectreactor:reactor-core:3.3.4.RELEASE
20 | org.json:json:20190722
21 | org.reactivestreams:reactive-streams:1.0.3
22 | org.slf4j:slf4j-api:1.7.30
23 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
24 | org.springframework:spring-beans:5.2.5.RELEASE
25 | org.springframework:spring-core:5.2.5.RELEASE
26 | org.springframework:spring-jcl:5.2.5.RELEASE
27 | org.springframework:spring-web:5.2.5.RELEASE
28 | org.springframework:spring-webflux:5.2.5.RELEASE
29 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/jacocoAgent.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/jacocoAnt.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 | org.jacoco:org.jacoco.ant:0.8.5
6 | org.jacoco:org.jacoco.core:0.8.5
7 | org.jacoco:org.jacoco.report:0.8.5
8 | org.ow2.asm:asm-analysis:7.2
9 | org.ow2.asm:asm-commons:7.2
10 | org.ow2.asm:asm-tree:7.2
11 | org.ow2.asm:asm:7.2
12 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/runtime.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/runtimeClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.jcabi:jcabi-log:0.14
5 | com.jcabi:jcabi-manifests:1.1
6 | io.netty:netty-buffer:4.1.48.Final
7 | io.netty:netty-codec-http2:4.1.48.Final
8 | io.netty:netty-codec-http:4.1.48.Final
9 | io.netty:netty-codec-socks:4.1.48.Final
10 | io.netty:netty-codec:4.1.48.Final
11 | io.netty:netty-common:4.1.48.Final
12 | io.netty:netty-handler-proxy:4.1.48.Final
13 | io.netty:netty-handler:4.1.48.Final
14 | io.netty:netty-resolver:4.1.48.Final
15 | io.netty:netty-transport-native-epoll:4.1.48.Final
16 | io.netty:netty-transport-native-unix-common:4.1.48.Final
17 | io.netty:netty-transport:4.1.48.Final
18 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
19 | io.projectreactor:reactor-core:3.3.4.RELEASE
20 | org.json:json:20190722
21 | org.reactivestreams:reactive-streams:1.0.3
22 | org.slf4j:slf4j-api:1.7.30
23 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
24 | org.springframework:spring-beans:5.2.5.RELEASE
25 | org.springframework:spring-core:5.2.5.RELEASE
26 | org.springframework:spring-jcl:5.2.5.RELEASE
27 | org.springframework:spring-web:5.2.5.RELEASE
28 | org.springframework:spring-webflux:5.2.5.RELEASE
29 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/testAnnotationProcessor.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/testCompile.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/testCompileClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | com.jcabi:jcabi-log:0.14
5 | com.jcabi:jcabi-manifests:1.1
6 | io.netty:netty-buffer:4.1.48.Final
7 | io.netty:netty-codec-http2:4.1.48.Final
8 | io.netty:netty-codec-http:4.1.48.Final
9 | io.netty:netty-codec-socks:4.1.48.Final
10 | io.netty:netty-codec:4.1.48.Final
11 | io.netty:netty-common:4.1.48.Final
12 | io.netty:netty-handler-proxy:4.1.48.Final
13 | io.netty:netty-handler:4.1.48.Final
14 | io.netty:netty-resolver:4.1.48.Final
15 | io.netty:netty-transport-native-epoll:4.1.48.Final
16 | io.netty:netty-transport-native-unix-common:4.1.48.Final
17 | io.netty:netty-transport:4.1.48.Final
18 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
19 | io.projectreactor:reactor-core:3.3.4.RELEASE
20 | org.apiguardian:apiguardian-api:1.1.0
21 | org.json:json:20190722
22 | org.junit.jupiter:junit-jupiter-api:5.5.2
23 | org.junit.platform:junit-platform-commons:1.5.2
24 | org.opentest4j:opentest4j:1.2.0
25 | org.reactivestreams:reactive-streams:1.0.3
26 | org.slf4j:slf4j-api:1.7.30
27 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
28 | org.springframework:spring-beans:5.2.5.RELEASE
29 | org.springframework:spring-core:5.2.5.RELEASE
30 | org.springframework:spring-jcl:5.2.5.RELEASE
31 | org.springframework:spring-web:5.2.5.RELEASE
32 | org.springframework:spring-webflux:5.2.5.RELEASE
33 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/testCompileOnly.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/testRuntime.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/impl/gradle/dependency-locks/testRuntimeClasspath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | ch.qos.logback:logback-classic:1.2.3
5 | ch.qos.logback:logback-core:1.2.3
6 | com.jcabi:jcabi-log:0.14
7 | com.jcabi:jcabi-manifests:1.1
8 | io.netty:netty-buffer:4.1.48.Final
9 | io.netty:netty-codec-http2:4.1.48.Final
10 | io.netty:netty-codec-http:4.1.48.Final
11 | io.netty:netty-codec-socks:4.1.48.Final
12 | io.netty:netty-codec:4.1.48.Final
13 | io.netty:netty-common:4.1.48.Final
14 | io.netty:netty-handler-proxy:4.1.48.Final
15 | io.netty:netty-handler:4.1.48.Final
16 | io.netty:netty-resolver:4.1.48.Final
17 | io.netty:netty-transport-native-epoll:4.1.48.Final
18 | io.netty:netty-transport-native-unix-common:4.1.48.Final
19 | io.netty:netty-transport:4.1.48.Final
20 | io.projectreactor.netty:reactor-netty:0.9.6.RELEASE
21 | io.projectreactor:reactor-core:3.3.4.RELEASE
22 | org.apiguardian:apiguardian-api:1.1.0
23 | org.json:json:20190722
24 | org.junit.jupiter:junit-jupiter-api:5.5.2
25 | org.junit.jupiter:junit-jupiter-engine:5.5.2
26 | org.junit.platform:junit-platform-commons:1.5.2
27 | org.junit.platform:junit-platform-engine:1.5.2
28 | org.opentest4j:opentest4j:1.2.0
29 | org.reactivestreams:reactive-streams:1.0.3
30 | org.slf4j:slf4j-api:1.7.30
31 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
32 | org.springframework:spring-beans:5.2.5.RELEASE
33 | org.springframework:spring-core:5.2.5.RELEASE
34 | org.springframework:spring-jcl:5.2.5.RELEASE
35 | org.springframework:spring-web:5.2.5.RELEASE
36 | org.springframework:spring-webflux:5.2.5.RELEASE
37 |
--------------------------------------------------------------------------------
/impl/src/main/java/net/dv8tion/jda/api/audio/AudioPacket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2018 Austin Keener & Michael Ritter & Florian Spieß
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.dv8tion.jda.api.audio;
18 |
19 | import com.iwebpp.crypto.TweetNaclFast;
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | import javax.annotation.Nonnull;
24 | import java.nio.Buffer;
25 | import java.nio.ByteBuffer;
26 |
27 | /**
28 | * Represents the contents of a audio packet that was either received from Discord or
29 | * will be sent to discord.
30 | */
31 | public class AudioPacket
32 | {
33 | private static final Logger log = LoggerFactory.getLogger(AudioPacket.class);
34 |
35 | public static final int RTP_HEADER_BYTE_LENGTH = 12;
36 |
37 | /**
38 | * Bit index 0 and 1 represent the RTP Protocol version used. Discord uses the latest RTP protocol version, 2.
39 | * Bit index 2 represents whether or not we pad. Opus uses an internal padding system, so RTP padding is not used.
40 | * Bit index 3 represents if we use extensions. Discord does not use RTP extensions.
41 | * Bit index 4 to 7 represent the CC or CSRC count. CSRC is Combined SSRC. Discord doesn't combine audio streams,
42 | * so the Combined count will always be 0 (binary: 0000).
43 | * This byte should always be the same, no matter the library implementation.
44 | */
45 | public static final byte RTP_VERSION_PAD_EXTEND = (byte) 0x80; //Binary: 1000 0000
46 |
47 | /**
48 | * This is Discord's RTP Profile Payload type.
49 | * I've yet to find actual documentation on what the bits inside this value represent.
50 | * As far as I can tell, this byte will always be the same, no matter the library implementation.
51 | */
52 | public static final byte RTP_PAYLOAD_TYPE = (byte) 0x78; //Binary: 0100 1000
53 |
54 | private final char seq;
55 | private final int timestamp;
56 | private final int ssrc;
57 | private final ByteBuffer encodedAudio;
58 | private final byte[] rawPacket;
59 |
60 | public AudioPacket(final char seq, final int timestamp, final int ssrc, final ByteBuffer encodedAudio)
61 | {
62 | this.seq = seq;
63 | this.ssrc = ssrc;
64 | this.timestamp = timestamp;
65 | this.encodedAudio = encodedAudio;
66 | this.rawPacket = generateRawPacket(seq, timestamp, ssrc, encodedAudio);
67 | }
68 |
69 | public byte[] getNoncePadded()
70 | {
71 | final byte[] nonce = new byte[TweetNaclFast.SecretBox.nonceLength];
72 | //The first 12 bytes are the rawPacket are the RTP Discord Nonce.
73 | System.arraycopy(this.rawPacket, 0, nonce, 0, RTP_HEADER_BYTE_LENGTH);
74 | return nonce;
75 | }
76 |
77 | //this may reallocate the passed bytebuffer if it is too small
78 | public ByteBuffer asEncryptedPacket(final ByteBuffer buffer, final byte[] secretKey, @Nonnull final byte[] nonce, final int nonceLength)
79 | {
80 | ByteBuffer outputBuffer = buffer;
81 | //Xsalsa20's Nonce is 24 bytes long, however RTP (and consequently Discord)'s nonce is a different length
82 | // so we need to create a 24 byte array, and copy the nonce into it.
83 | // we will leave the extra bytes as nulls. (Java sets non-populated bytes as 0).
84 | byte[] extendedNonce = nonce;
85 | if (nonceLength == 0) {
86 | extendedNonce = getNoncePadded();
87 | }
88 | final byte[] array = encodedAudio.array();
89 | final int arrayOffset = encodedAudio.arrayOffset();
90 | final int length = encodedAudio.remaining();
91 |
92 | //Create our SecretBox encoder with the secretKey provided by Discord.
93 | final TweetNaclFast.SecretBox boxer = new TweetNaclFast.SecretBox(secretKey);
94 | final byte[] encryptedAudio = boxer.box(array, arrayOffset, length, extendedNonce);
95 | outputBuffer.clear();
96 | final int capacity = RTP_HEADER_BYTE_LENGTH + encryptedAudio.length + nonceLength;
97 | if (capacity > outputBuffer.remaining()) {
98 | log.trace("Allocating byte buffer with capacity " + capacity);
99 | outputBuffer = ByteBuffer.allocate(capacity);
100 | }
101 | populateBuffer(this.seq, this.timestamp, this.ssrc, ByteBuffer.wrap(encryptedAudio), outputBuffer);
102 | if (nonceLength > 0) {
103 | outputBuffer.put(nonce, 0, nonceLength);
104 | }
105 |
106 | ((Buffer) outputBuffer).flip();
107 | return outputBuffer;
108 | }
109 |
110 | private static byte[] generateRawPacket(final char seq, final int timestamp, final int ssrc, final ByteBuffer data)
111 | {
112 | final ByteBuffer buffer = ByteBuffer.allocate(RTP_HEADER_BYTE_LENGTH + data.remaining());
113 | populateBuffer(seq, timestamp, ssrc, data, buffer);
114 | return buffer.array();
115 | }
116 |
117 | private static void populateBuffer(final char seq, final int timestamp, final int ssrc, final ByteBuffer data, final ByteBuffer buffer)
118 | {
119 | buffer.put(RTP_VERSION_PAD_EXTEND);
120 | buffer.put(RTP_PAYLOAD_TYPE);
121 | buffer.putChar(seq);
122 | buffer.putInt(timestamp);
123 | buffer.putInt(ssrc);
124 | buffer.put(data);
125 | ((Buffer) data).flip();
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/AudioStackLifecyclePipeline.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl;
18 |
19 | import edu.umd.cs.findbugs.annotations.CheckReturnValue;
20 | import net.dv8tion.jda.api.audio.factory.IAudioSendFactory;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 | import reactor.core.publisher.BaseSubscriber;
24 | import space.npstr.magma.api.MagmaMember;
25 | import space.npstr.magma.api.MagmaWebsocketConnectionState;
26 | import space.npstr.magma.api.Member;
27 | import space.npstr.magma.api.WebsocketConnectionState;
28 | import space.npstr.magma.api.event.MagmaEvent;
29 | import space.npstr.magma.impl.connections.AudioConnection;
30 | import space.npstr.magma.impl.connections.AudioWebSocket;
31 | import space.npstr.magma.impl.connections.hax.ClosingWebSocketClient;
32 | import space.npstr.magma.impl.events.audio.lifecycle.CloseWebSocket;
33 | import space.npstr.magma.impl.events.audio.lifecycle.ConnectWebSocketLcEvent;
34 | import space.npstr.magma.impl.events.audio.lifecycle.LifecycleEvent;
35 | import space.npstr.magma.impl.events.audio.lifecycle.Shutdown;
36 | import space.npstr.magma.impl.events.audio.lifecycle.UpdateSendHandler;
37 | import space.npstr.magma.impl.events.audio.lifecycle.UpdateSpeakingMode;
38 | import space.npstr.magma.impl.events.audio.lifecycle.VoiceServerUpdate;
39 | import space.npstr.magma.impl.immutables.ImmutableSessionInfo;
40 |
41 | import java.net.DatagramSocket;
42 | import java.util.List;
43 | import java.util.Map;
44 | import java.util.concurrent.ConcurrentHashMap;
45 | import java.util.function.Consumer;
46 | import java.util.function.Function;
47 | import java.util.stream.Collectors;
48 |
49 | /**
50 | * Created by napster on 22.04.18.
51 | *
52 | * This class manages the lifecycles of various AudioStack objects.
53 | *
54 | * The {@link AudioStack} consists of:
55 | *
56 | * - A websocket connection ( {@literal ->} {@link AudioWebSocket}
57 | * - A voice packet emitter ( {@literal ->} {@link AudioConnection}
58 | * - A send handler ( {@literal ->} {@link net.dv8tion.jda.api.audio.AudioSendHandler}, provided by user code)
59 | * - A send system ( {@literal ->} {@link net.dv8tion.jda.api.audio.factory.IAudioSendSystem}, provided by user code)
60 | *
61 | *
62 | * Lifecycle Events
63 | *
64 | * - Constructive Events
65 | *
66 | * - VoiceServerUpdate telling us to connect to a voice server
67 | * - Reconnects following certain close events
68 | *
69 | *
70 | * - Destructive Events:
71 | *
72 | * - Websocket close events
73 | * - Shutdown
74 | *
75 | *
76 | * - Neutral Events
77 | *
78 | * - Setting and removing a send handler
79 | *
80 | *
81 | *
82 | */
83 | public class AudioStackLifecyclePipeline extends BaseSubscriber {
84 |
85 | private static final Logger log = LoggerFactory.getLogger(AudioStackLifecyclePipeline.class);
86 |
87 | // userId <-> guildId <-> audio stack
88 | private final Map> audioStacks = new ConcurrentHashMap<>();
89 |
90 | private final Function sendFactoryProvider;
91 | private final ClosingWebSocketClient webSocketClient;
92 | private final Consumer apiEventConsumer;
93 | private final DatagramSocket udpSocket;
94 |
95 | public AudioStackLifecyclePipeline(final Function sendFactoryProvider,
96 | final ClosingWebSocketClient webSocketClient,
97 | final Consumer apiEventConsumer,
98 | final DatagramSocket udpSocket) {
99 | this.sendFactoryProvider = sendFactoryProvider;
100 | this.webSocketClient = webSocketClient;
101 | this.apiEventConsumer = apiEventConsumer;
102 | this.udpSocket = udpSocket;
103 | }
104 |
105 | @Override
106 | protected void hookOnNext(final LifecycleEvent event) {
107 | if (event instanceof VoiceServerUpdate) {
108 | final VoiceServerUpdate voiceServerUpdate = (VoiceServerUpdate) event;
109 | this.getAudioStack(event)
110 | .next(ConnectWebSocketLcEvent.builder()
111 | .sessionInfo(ImmutableSessionInfo.builder()
112 | .voiceServerUpdate(voiceServerUpdate)
113 | .build())
114 | .build()
115 | );
116 | } else if (event instanceof UpdateSendHandler) {
117 | this.getAudioStack(event)
118 | .next(event);
119 | } else if (event instanceof CloseWebSocket) {
120 | //pass it on
121 | this.apiEventConsumer.accept(((CloseWebSocket) event).getApiEvent());
122 | this.getAudioStack(event)
123 | .next(event);
124 | } else if (event instanceof Shutdown) {
125 | this.dispose();
126 |
127 | this.audioStacks.values().stream().flatMap(map -> map.values().stream()).forEach(
128 | audioStack -> audioStack.next(event)
129 | );
130 | } else if (event instanceof UpdateSpeakingMode) {
131 | this.getAudioStack(event)
132 | .next(event);
133 | } else {
134 | log.warn("Unhandled lifecycle event of class {}", event.getClass().getSimpleName());
135 | }
136 | }
137 |
138 | @CheckReturnValue
139 | public List getAudioConnectionStates() {
140 | return this.audioStacks.entrySet().stream()
141 | .flatMap(outerEntry -> {
142 | final String userId = outerEntry.getKey();
143 | return outerEntry.getValue().entrySet().stream()
144 | .map(innerEntry -> {
145 | final String guildId = innerEntry.getKey();
146 | final AudioStack audioStack = innerEntry.getValue();
147 | return MagmaWebsocketConnectionState.builder()
148 | .member(MagmaMember.builder()
149 | .userId(userId)
150 | .guildId(guildId)
151 | .build())
152 | .phase(audioStack.getConnectionPhase())
153 | .build();
154 | });
155 | })
156 | .collect(Collectors.toList());
157 | }
158 |
159 | @CheckReturnValue
160 | @SuppressWarnings("squid:S00117")
161 | private AudioStack getAudioStack(final LifecycleEvent lifecycleEvent) {
162 | return this.audioStacks
163 | .computeIfAbsent(lifecycleEvent.getUserId(), __ -> new ConcurrentHashMap<>())
164 | .computeIfAbsent(lifecycleEvent.getGuildId(), __ ->
165 | new AudioStack(lifecycleEvent.getMember(),
166 | this.sendFactoryProvider.apply(lifecycleEvent.getMember()),
167 | this.webSocketClient,
168 | this.apiEventConsumer,
169 | this.udpSocket));
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/EncryptionMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl;
18 |
19 | import org.json.JSONArray;
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | import java.util.ArrayList;
24 | import java.util.Collection;
25 | import java.util.Comparator;
26 | import java.util.List;
27 | import java.util.Optional;
28 |
29 | public enum EncryptionMode {
30 |
31 | XSALSA20_POLY1305_LITE(30), // uses 4 byte nonce instead of 24 bytes
32 | XSALSA20_POLY1305_SUFFIX(20), // "official" implementation using random 24 byte nonce
33 | XSALSA20_POLY1305(10); // unofficial implementation using time stamps (?) as nonce (24 bytes total)
34 |
35 | private static final Logger log = LoggerFactory.getLogger(EncryptionMode.class);
36 | private static final Comparator preferenceComparator = Comparator.comparingInt(EncryptionMode::getPreference).reversed();
37 |
38 | private final int preference;
39 | private final String key;
40 |
41 | EncryptionMode(final int preference) {
42 | this.preference = preference;
43 | this.key = this.name().toLowerCase();
44 | }
45 |
46 | /**
47 | * @return the key that discord recognizes this mode under
48 | */
49 | public String getKey() {
50 | return this.key;
51 | }
52 |
53 | /**
54 | * @return the preference indicator
55 | */
56 | public int getPreference()
57 | {
58 | return preference;
59 | }
60 |
61 | /**
62 | * @return The encryption mode corresponding to the given input, or nothing.
63 | */
64 | public static Optional parse(final String input) {
65 | try {
66 | return Optional.of(EncryptionMode.valueOf(input.toUpperCase()));
67 | } catch (final IllegalArgumentException e) {
68 | log.debug("Could not parse encryption mode: {}", input);
69 | }
70 |
71 | return Optional.empty();
72 | }
73 |
74 | /**
75 | * @return parse a JSONArray into a list of encryption modes
76 | */
77 | public static List fromJson(final JSONArray array) {
78 | final List result = new ArrayList<>();
79 | for (final Object o : array) {
80 | try {
81 | parse((String) o).ifPresent(result::add);
82 | } catch (final IllegalArgumentException ignored) {
83 | //ignored
84 | }
85 | }
86 | return result;
87 | }
88 |
89 | /**
90 | * @return parse a JSONArray into a list of encryption modes
91 | */
92 | public static Optional getPreferredMode(final Collection encryptionModes) {
93 | if (encryptionModes.isEmpty()) {
94 | log.warn("Can not pick a preferred encryption mode from an empty collection");
95 | return Optional.empty();
96 | }
97 | final List sort = new ArrayList<>(encryptionModes);
98 | sort.sort(preferenceComparator);
99 | return Optional.of(sort.get(0));
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/Magma.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl;
18 |
19 | import edu.umd.cs.findbugs.annotations.Nullable;
20 | import java.net.DatagramSocket;
21 | import java.net.SocketException;
22 | import java.util.List;
23 | import java.util.Optional;
24 | import java.util.Set;
25 | import java.util.function.Function;
26 | import java.util.logging.Level;
27 | import net.dv8tion.jda.api.audio.AudioSendHandler;
28 | import net.dv8tion.jda.api.audio.factory.IAudioSendFactory;
29 | import org.slf4j.Logger;
30 | import org.slf4j.LoggerFactory;
31 | import reactor.core.publisher.Flux;
32 | import reactor.core.publisher.FluxSink;
33 | import reactor.core.publisher.UnicastProcessor;
34 | import reactor.core.scheduler.Schedulers;
35 | import space.npstr.magma.api.MagmaApi;
36 | import space.npstr.magma.api.Member;
37 | import space.npstr.magma.api.ServerUpdate;
38 | import space.npstr.magma.api.SpeakingMode;
39 | import space.npstr.magma.api.WebsocketConnectionState;
40 | import space.npstr.magma.api.event.MagmaEvent;
41 | import space.npstr.magma.api.event.WebSocketClosedApiEvent;
42 | import space.npstr.magma.impl.connections.hax.ClosingReactorNettyWebSocketClient;
43 | import space.npstr.magma.impl.connections.hax.ClosingWebSocketClient;
44 | import space.npstr.magma.impl.events.audio.lifecycle.CloseWebSocketLcEvent;
45 | import space.npstr.magma.impl.events.audio.lifecycle.LifecycleEvent;
46 | import space.npstr.magma.impl.events.audio.lifecycle.Shutdown;
47 | import space.npstr.magma.impl.events.audio.lifecycle.UpdateSendHandlerLcEvent;
48 | import space.npstr.magma.impl.events.audio.lifecycle.UpdateSpeakingModeLcEvent;
49 | import space.npstr.magma.impl.events.audio.lifecycle.VoiceServerUpdateLcEvent;
50 |
51 | public class Magma implements MagmaApi {
52 |
53 | private static final Logger log = LoggerFactory.getLogger(Magma.class);
54 |
55 | private final FluxSink lifecycleSink;
56 | @Nullable
57 | private FluxSink apiEventSink = null;
58 | private final Flux apiEventFlux = Flux.create(sink -> this.apiEventSink = sink);
59 | private final AudioStackLifecyclePipeline lifecyclePipeline;
60 | private final DatagramSocket udpSocket;
61 |
62 | /**
63 | * @see MagmaApi
64 | */
65 | public Magma(final Function sendFactoryProvider) {
66 | final ClosingWebSocketClient webSocketClient = new ClosingReactorNettyWebSocketClient();
67 | try {
68 | this.udpSocket = new DatagramSocket();
69 | } catch (final SocketException e) {
70 | throw new RuntimeException("Failed to set up datagram socket", e);
71 | }
72 |
73 | this.lifecyclePipeline = new AudioStackLifecyclePipeline(
74 | sendFactoryProvider,
75 | webSocketClient,
76 | magmaEvent -> {
77 | if (this.apiEventSink != null) this.apiEventSink.next(magmaEvent);
78 | },
79 | this.udpSocket
80 | );
81 |
82 | final UnicastProcessor processor = UnicastProcessor.create();
83 | this.lifecycleSink = processor.sink();
84 | processor
85 | .log(log.getName(), Level.FINEST) //FINEST = TRACE
86 | .publishOn(Schedulers.parallel())
87 | .subscribe(this.lifecyclePipeline);
88 | }
89 |
90 | // ################################################################################
91 | // # Public API
92 | // ################################################################################
93 |
94 | @Override
95 | public DatagramSocket getDatagramSocket() {
96 | return this.udpSocket;
97 | }
98 |
99 | @Override
100 | public void shutdown() {
101 | this.lifecycleSink.next(Shutdown.INSTANCE);
102 | if (this.apiEventSink != null) this.apiEventSink.complete();
103 | this.udpSocket.close();
104 | }
105 |
106 | @Override
107 | public Flux getEventStream() {
108 | return this.apiEventFlux;
109 | }
110 |
111 | @Override
112 | public void provideVoiceServerUpdate(final Member member, final ServerUpdate serverUpdate) {
113 | this.lifecycleSink.next(VoiceServerUpdateLcEvent.builder()
114 | .member(member)
115 | .sessionId(serverUpdate.getSessionId())
116 | .endpoint(serverUpdate.getEndpoint().replace(":80", "")) //Strip the port from the endpoint.
117 | .token(serverUpdate.getToken())
118 | .build());
119 | }
120 |
121 | @Override
122 | public void setSendHandler(final Member member, final AudioSendHandler sendHandler) {
123 | this.updateSendHandler(member, sendHandler);
124 | }
125 |
126 | @Override
127 | public void setSpeakingMode(final Member member, @Nullable final Set mode) {
128 | this.lifecycleSink.next(UpdateSpeakingModeLcEvent.builder()
129 | .member(member)
130 | .speakingModes(mode)
131 | .build());
132 | }
133 |
134 | @Override
135 | public void removeSendHandler(final Member member) {
136 | this.updateSendHandler(member, null);
137 | }
138 |
139 | @Override
140 | public void closeConnection(final Member member) {
141 | this.lifecycleSink.next(CloseWebSocketLcEvent.builder()
142 | .member(member)
143 | .apiEvent(WebSocketClosedApiEvent.builder()
144 | .member(member)
145 | .closeCode(1000)
146 | .reason("Closed by client")
147 | .isByRemote(false)
148 | .build())
149 | .build());
150 | }
151 |
152 | @Override
153 | public List getAudioConnectionStates() {
154 | return this.lifecyclePipeline.getAudioConnectionStates();
155 | }
156 |
157 | // ################################################################################
158 | // # Internals
159 | // ################################################################################
160 |
161 | private void updateSendHandler(final Member member, @Nullable final AudioSendHandler sendHandler) {
162 | this.lifecycleSink.next(UpdateSendHandlerLcEvent.builder()
163 | .member(member)
164 | .audioSendHandler(Optional.ofNullable(sendHandler))
165 | .build());
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/MagmaVersionProvider.java:
--------------------------------------------------------------------------------
1 | package space.npstr.magma.impl;
2 |
3 | import com.jcabi.manifests.Manifests;
4 |
5 | public class MagmaVersionProvider {
6 |
7 | // See gradle build file where this is written into the manifest
8 | private static final String MAGMA_VERSION_KEY = "Magma-Version";
9 |
10 | private static final String FALLBACK_VERSION = "unknown version";
11 |
12 | public String getVersion() {
13 |
14 | if (!Manifests.exists(MAGMA_VERSION_KEY)) {
15 | return FALLBACK_VERSION;
16 | }
17 |
18 | return Manifests.read(MAGMA_VERSION_KEY);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/connections/AudioWebSocketSessionHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.connections;
18 |
19 | import edu.umd.cs.findbugs.annotations.Nullable;
20 | import io.netty.handler.codec.http.websocketx.WebSocketCloseStatus;
21 | import org.json.JSONObject;
22 | import org.reactivestreams.Subscriber;
23 | import org.slf4j.Logger;
24 | import org.slf4j.LoggerFactory;
25 | import org.springframework.web.reactive.socket.WebSocketHandler;
26 | import org.springframework.web.reactive.socket.WebSocketMessage;
27 | import org.springframework.web.reactive.socket.WebSocketSession;
28 | import reactor.core.publisher.BaseSubscriber;
29 | import reactor.core.publisher.Flux;
30 | import reactor.core.publisher.FluxSink;
31 | import reactor.core.publisher.Mono;
32 | import reactor.core.publisher.UnicastProcessor;
33 | import reactor.core.scheduler.Schedulers;
34 | import space.npstr.magma.impl.connections.hax.ClosingReactorNettyWebSocketSession;
35 | import space.npstr.magma.impl.events.audio.ws.OpCode;
36 | import space.npstr.magma.impl.events.audio.ws.in.InboundWsEvent;
37 | import space.npstr.magma.impl.events.audio.ws.out.OutboundWsEvent;
38 |
39 | import java.util.Objects;
40 | import java.util.concurrent.atomic.AtomicReference;
41 | import java.util.logging.Level;
42 |
43 | /**
44 | * Created by napster on 21.04.18.
45 | */
46 | public class AudioWebSocketSessionHandler extends BaseSubscriber implements WebSocketHandler {
47 | private static final Logger log = LoggerFactory.getLogger(AudioWebSocketSessionHandler.class);
48 | private final Subscriber inbound;
49 |
50 | private final IntermediaryPipeHolder pipes = new IntermediaryPipeHolder();
51 | @Nullable
52 | private WebSocketSession session;
53 |
54 | /**
55 | * @param inbound
56 | * Subscriber to the events we will receive from Discord
57 | */
58 | public AudioWebSocketSessionHandler(final Subscriber inbound) {
59 | this.prepareConnect();
60 | this.inbound = inbound;
61 | }
62 |
63 | /**
64 | * Close the session of this handler, if there is any.
65 | */
66 | public void close() {
67 | if (this.session != null) {
68 | this.session.close()
69 | .publishOn(Schedulers.parallel())
70 | .subscribe();
71 | }
72 | }
73 |
74 | /**
75 | * Call this when planning to reuse this handler for another session.
76 | * We need to replace the intermediary processor so that the new session can be subscribed to the original
77 | * {@code Flux outbound} constructor parameter.
78 | *
79 | * Any outbound events buffered in the old processor will be lost upon calling this, which is ok,
80 | * given that this method is expected to be called when the connection has been closed.
81 | */
82 | public void prepareConnect() {
83 | final UnicastProcessor intermediaryProcessor = UnicastProcessor.create();
84 | this.pipes.setIntermediaryOutboundSink(intermediaryProcessor.sink());
85 | this.pipes.setIntermediaryOutboundFlux(intermediaryProcessor);
86 | }
87 |
88 | @Override
89 | @SuppressWarnings("squid:CommentedOutCodeLine")
90 | public Mono handle(final WebSocketSession session) {
91 |
92 | // * * *
93 | // ATTENTION: Live testing code for resuming. Do not commit uncommented
94 | // Mono.delay(Duration.ofSeconds(30))
95 | // .subscribe(tick -> {
96 | // session.close(new CloseStatus(CloseCode.VOICE_SERVER_CRASHED, "lol"));
97 | // ((AudioWebSocket) this.inbound).hookOnNext(WebSocketClosedWsEvent.builder()
98 | // .code(CloseCode.VOICE_SERVER_CRASHED)
99 | // .reason("lol")
100 | // .build());
101 | // });
102 | // * * *
103 |
104 | this.session = session;
105 | log.trace("Handshake: {}", session.getHandshakeInfo());
106 |
107 | Flux messages = session.receive()
108 | .map(WebSocketMessage::getPayloadAsText);
109 |
110 | ClosingReactorNettyWebSocketSession reactorNettyWebSocketSession = (ClosingReactorNettyWebSocketSession) session;
111 | Mono closeMessage = reactorNettyWebSocketSession.getDelegate().getInbound().receiveCloseStatus()
112 | .map(this::toMagmaWebSocketEventPayload);
113 |
114 | Flux.mergeDelayError(1, messages, closeMessage)
115 | .log(log.getName() + ".>>>", Level.FINEST) //FINEST = TRACE
116 | .map(InboundWsEvent::from)
117 | .doOnTerminate(() -> log.trace("Receiving terminated"))
118 | .publishOn(Schedulers.parallel())
119 | .subscribe(this.inbound);
120 |
121 | return session
122 | .send(this.pipes.getIntermediaryOutboundFlux()
123 | .map(OutboundWsEvent::asMessage)
124 | .log(log.getName() + ".<<<", Level.FINEST) //FINEST = TRACE
125 | .map(session::textMessage)
126 | )
127 | .doOnTerminate(() -> log.trace("Sending terminated"));
128 | }
129 |
130 | @Override
131 | protected void hookOnNext(final OutboundWsEvent event) {
132 | this.pipes.getIntermediaryOutboundSink().next(event);
133 | }
134 |
135 | /**
136 | * Transform a websocket close status into a payload that will be parsed in {@link space.npstr.magma.impl.events.audio.ws.in.InboundWsEvent#from}
137 | */
138 | private String toMagmaWebSocketEventPayload(WebSocketCloseStatus closeStatus) {
139 | return new JSONObject()
140 | .put("op", OpCode.WEBSOCKET_CLOSE)
141 | .put("d", new JSONObject()
142 | .put("code", closeStatus.code())
143 | .put("reason", closeStatus.reasonText()))
144 | .toString();
145 | }
146 |
147 | /**
148 | * Helper class to take care of volatile & null checks
149 | */
150 | private static class IntermediaryPipeHolder {
151 |
152 | private final AtomicReference> intermediaryOutboundFlux = new AtomicReference<>();
153 | private final AtomicReference> intermediaryOutboundSink = new AtomicReference<>();
154 |
155 | private Flux getIntermediaryOutboundFlux() {
156 | return Objects.requireNonNull(this.intermediaryOutboundFlux.get(), "Using the intermediary outbound flux before it has been prepared");
157 | }
158 |
159 | private void setIntermediaryOutboundFlux(final Flux intermediaryOutboundFlux) {
160 | this.intermediaryOutboundFlux.set(intermediaryOutboundFlux);
161 | }
162 |
163 | private FluxSink getIntermediaryOutboundSink() {
164 | return Objects.requireNonNull(this.intermediaryOutboundSink.get(), "Using the intermediary outbound sink before it has been prepared");
165 | }
166 |
167 | private void setIntermediaryOutboundSink(final FluxSink intermediaryOutboundSink) {
168 | this.intermediaryOutboundSink.set(intermediaryOutboundSink);
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/connections/hax/ClosingReactorNettyWebSocketClient.java:
--------------------------------------------------------------------------------
1 | package space.npstr.magma.impl.connections.hax;
2 |
3 | import java.net.URI;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.core.io.buffer.NettyDataBufferFactory;
7 | import org.springframework.http.HttpHeaders;
8 | import org.springframework.util.StringUtils;
9 | import org.springframework.web.reactive.socket.HandshakeInfo;
10 | import org.springframework.web.reactive.socket.WebSocketHandler;
11 | import org.springframework.web.reactive.socket.WebSocketSession;
12 | import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
13 | import reactor.core.publisher.Mono;
14 | import reactor.netty.http.websocket.WebsocketInbound;
15 |
16 | /**
17 | * Allow our {@link space.npstr.magma.impl.connections.AudioWebSocketSessionHandler} us to get its hands on the close code.
18 | *
19 | * Plugs in our custom {@link ClosingReactorNettyWebSocketSession}.
20 | *
21 | * Rest of the file is copied from the superclass(es) that is necessary to make that work.
22 | */
23 | public class ClosingReactorNettyWebSocketClient extends ReactorNettyWebSocketClient implements ClosingWebSocketClient {
24 |
25 | private static final Logger logger = LoggerFactory.getLogger(ClosingReactorNettyWebSocketClient.class);
26 |
27 | @Override
28 | public Mono execute(URI url, HttpHeaders requestHeaders, WebSocketHandler handler) {
29 | return getHttpClient()
30 | .headers(nettyHeaders -> setNettyHeaders(requestHeaders, nettyHeaders))
31 | .websocket(StringUtils.collectionToCommaDelimitedString(handler.getSubProtocols()))
32 | .uri(url.toString())
33 | .handle((inbound, outbound) -> {
34 | HttpHeaders responseHeaders = toHttpHeaders(inbound);
35 | String protocol = responseHeaders.getFirst("Sec-WebSocket-Protocol");
36 | HandshakeInfo info = new HandshakeInfo(url, responseHeaders, Mono.empty(), protocol);
37 | NettyDataBufferFactory factory = new NettyDataBufferFactory(outbound.alloc());
38 |
39 | // * * * * *
40 | // plug in our custom websocket session
41 |
42 | WebSocketSession session = new ClosingReactorNettyWebSocketSession(inbound, outbound, info, factory);
43 |
44 | // * * * * *
45 | if (logger.isDebugEnabled()) {
46 | logger.debug("Started session '" + session.getId() + "' for " + url);
47 | }
48 | return handler.handle(session);
49 | })
50 | .doOnRequest(n -> {
51 | if (logger.isDebugEnabled()) {
52 | logger.debug("Connecting to " + url);
53 | }
54 | })
55 | .next();
56 | }
57 |
58 | private void setNettyHeaders(HttpHeaders httpHeaders, io.netty.handler.codec.http.HttpHeaders nettyHeaders) {
59 | httpHeaders.forEach(nettyHeaders::set);
60 | }
61 |
62 | private HttpHeaders toHttpHeaders(WebsocketInbound inbound) {
63 | HttpHeaders headers = new HttpHeaders();
64 | io.netty.handler.codec.http.HttpHeaders nettyHeaders = inbound.headers();
65 | nettyHeaders.forEach(entry -> {
66 | String name = entry.getKey();
67 | headers.put(name, nettyHeaders.getAll(name));
68 | });
69 | return headers;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/connections/hax/ClosingReactorNettyWebSocketSession.java:
--------------------------------------------------------------------------------
1 | package space.npstr.magma.impl.connections.hax;
2 |
3 | import org.springframework.core.io.buffer.NettyDataBufferFactory;
4 | import org.springframework.web.reactive.socket.HandshakeInfo;
5 | import org.springframework.web.reactive.socket.adapter.ReactorNettyWebSocketSession;
6 | import reactor.netty.http.websocket.WebsocketInbound;
7 | import reactor.netty.http.websocket.WebsocketOutbound;
8 |
9 | /**
10 | * Allow our {@link space.npstr.magma.impl.connections.AudioWebSocketSessionHandler} us to get its hands on the close code.
11 | */
12 | public class ClosingReactorNettyWebSocketSession extends ReactorNettyWebSocketSession {
13 |
14 | public ClosingReactorNettyWebSocketSession(
15 | WebsocketInbound inbound,
16 | WebsocketOutbound outbound,
17 | HandshakeInfo info,
18 | NettyDataBufferFactory bufferFactory
19 | ) {
20 | super(inbound, outbound, info, bufferFactory);
21 | }
22 |
23 | @Override
24 | public WebSocketConnection getDelegate() {
25 | return super.getDelegate();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/connections/hax/ClosingWebSocketClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.connections.hax;
18 |
19 | import org.springframework.web.reactive.socket.client.WebSocketClient;
20 |
21 | /**
22 | * Created by napster on 21.06.18.
23 | *
24 | * Mark a class as enhanced by our code to properly relay the close events.
25 | */
26 | public interface ClosingWebSocketClient extends WebSocketClient {}
27 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/connections/hax/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.connections.hax;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/connections/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.connections;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/ConnectionEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | /**
20 | * Created by napster on 21.06.18.
21 | *
22 | * Events for the {@link space.npstr.magma.impl.connections.AudioConnection}
23 | */
24 | public interface ConnectionEvent {
25 | }
26 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/SetEncryptionMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | import space.npstr.magma.impl.EncryptionMode;
20 |
21 | /**
22 | * Created by napster on 21.06.18.
23 | */
24 | public interface SetEncryptionMode extends ConnectionEvent {
25 |
26 | EncryptionMode getEncryptionMode();
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/SetSecretKey.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | /**
20 | * Created by napster on 22.06.18.
21 | */
22 | public interface SetSecretKey extends ConnectionEvent {
23 |
24 | byte[] getSecretKey();
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/SetSsrc.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | /**
20 | * Created by napster on 21.06.18.
21 | */
22 | public interface SetSsrc extends ConnectionEvent {
23 |
24 | int getSsrc();
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/SetTargetAddress.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | import java.net.InetSocketAddress;
20 |
21 | /**
22 | * Created by napster on 22.06.18.
23 | */
24 | public interface SetTargetAddress extends ConnectionEvent {
25 |
26 | InetSocketAddress getTargetAddress();
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/Shutdown.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | /**
20 | * Created by napster on 21.06.18.
21 | */
22 | public class Shutdown implements ConnectionEvent {
23 |
24 | public static final Shutdown INSTANCE = new Shutdown();
25 |
26 | private Shutdown() {}
27 | }
28 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/UpdateSendHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | import net.dv8tion.jda.api.audio.AudioSendHandler;
20 |
21 | import java.util.Optional;
22 |
23 | /**
24 | * Created by napster on 21.06.18.
25 | */
26 | public interface UpdateSendHandler extends ConnectionEvent {
27 |
28 | Optional getAudioSendHandler();
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/UpdateSpeaking.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.conn;
18 |
19 | import space.npstr.magma.api.SpeakingMode;
20 |
21 | import java.util.Set;
22 |
23 | /**
24 | * Created by napster on 21.06.18.
25 | */
26 | public class UpdateSpeaking implements ConnectionEvent {
27 | private final boolean shouldSpeak;
28 | private final Set modes;
29 |
30 | public UpdateSpeaking(boolean shouldSpeak, Set modes) {
31 | this.shouldSpeak = shouldSpeak;
32 | this.modes = modes;
33 | }
34 |
35 | public boolean shouldSpeak() {
36 | return this.shouldSpeak;
37 | }
38 |
39 | public int getSpeakingMode() {
40 | return shouldSpeak ? SpeakingMode.toMask(modes) : 0;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/conn/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.events.audio.conn;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
25 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/CloseWebSocket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.lifecycle;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.api.event.WebSocketClosed;
21 | import space.npstr.magma.impl.immutables.ImmutableLcEvent;
22 |
23 | /**
24 | * Created by napster on 24.04.18.
25 | */
26 | @Value.Immutable
27 | @ImmutableLcEvent
28 | public abstract class CloseWebSocket implements LifecycleEvent {
29 |
30 | public abstract WebSocketClosed getApiEvent();
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/ConnectWebSocket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.lifecycle;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.api.Member;
21 | import space.npstr.magma.impl.immutables.ImmutableLcEvent;
22 | import space.npstr.magma.impl.immutables.SessionInfo;
23 |
24 | /**
25 | * Created by napster on 24.04.18.
26 | */
27 | @Value.Immutable
28 | @ImmutableLcEvent
29 | public abstract class ConnectWebSocket implements LifecycleEvent {
30 |
31 | @Override
32 | public Member getMember() {
33 | return this.getSessionInfo().getVoiceServerUpdate().getMember();
34 | }
35 |
36 | public abstract SessionInfo getSessionInfo();
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/LifecycleEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.lifecycle;
18 |
19 | import space.npstr.magma.api.Member;
20 |
21 | /**
22 | * Created by napster on 23.04.18.
23 | *
24 | * @see space.npstr.magma.impl.AudioStackLifecyclePipeline
25 | */
26 | public interface LifecycleEvent {
27 |
28 | Member getMember();
29 |
30 | default String getUserId() {
31 | return getMember().getUserId();
32 | }
33 |
34 | default String getGuildId() {
35 | return getMember().getGuildId();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/Shutdown.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.lifecycle;
18 |
19 | import space.npstr.magma.api.Member;
20 |
21 | /**
22 | * Created by napster on 24.04.18.
23 | *
24 | * This is a lifecycle event for the whole Magma API
25 | */
26 | public class Shutdown implements LifecycleEvent {
27 |
28 | public static final Shutdown INSTANCE = new Shutdown();
29 |
30 | private Shutdown() {
31 | }
32 |
33 | @Override
34 | public Member getMember() {
35 | throw new UnsupportedOperationException();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/UpdateSendHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.lifecycle;
18 |
19 | import net.dv8tion.jda.api.audio.AudioSendHandler;
20 | import org.immutables.value.Value;
21 | import space.npstr.magma.impl.immutables.ImmutableLcEvent;
22 |
23 | import java.util.Optional;
24 |
25 | /**
26 | * Created by napster on 24.04.18.
27 | */
28 | @Value.Immutable
29 | @ImmutableLcEvent
30 | public abstract class UpdateSendHandler implements LifecycleEvent {
31 |
32 | public abstract Optional getAudioSendHandler();
33 | }
34 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/UpdateSpeakingMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2019 Florian Spieß
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.lifecycle;
18 |
19 | import edu.umd.cs.findbugs.annotations.Nullable;
20 | import org.immutables.value.Value;
21 | import space.npstr.magma.api.SpeakingMode;
22 | import space.npstr.magma.impl.immutables.ImmutableLcEvent;
23 |
24 | import java.util.Set;
25 |
26 | @Value.Immutable
27 | @ImmutableLcEvent
28 | public abstract class UpdateSpeakingMode implements LifecycleEvent {
29 |
30 | @Nullable
31 | public abstract Set getSpeakingModes();
32 | }
33 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/VoiceServerUpdate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.lifecycle;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.immutables.ImmutableLcEvent;
21 |
22 | /**
23 | * Created by napster on 22.04.18.
24 | */
25 | @Value.Immutable
26 | @ImmutableLcEvent
27 | public abstract class VoiceServerUpdate implements LifecycleEvent {
28 |
29 | public abstract String getSessionId();
30 |
31 | public abstract String getEndpoint();
32 |
33 | public abstract String getToken();
34 | }
35 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/lifecycle/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.events.audio.lifecycle;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/CloseCode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws;
18 |
19 | import java.util.Optional;
20 |
21 | /**
22 | * Sources:
23 | * Flowchart
24 | * Discord Documentation
25 | */
26 | public enum CloseCode {
27 | //@formatter:off warn resume
28 | HEARTBEAT_TIMEOUT (1000, false, true),
29 | CLOUDFLARE (1001, false, true),
30 | ABNORMAL (1006, true, true),
31 |
32 | UNKNOWN_OP_CODE (4001, true, true),
33 | NOT_AUTHENTICATED (4003, true, false),
34 | AUTHENTICATION_FAILED (4004, true, false),
35 | ALREADY_AUTHENTICATED (4005, true, false),
36 | SESSION_NO_LONGER_VALID (4006, true, false),
37 | SESSION_TIMEOUT (4009, true, false),
38 | SERVER_NOT_FOUND (4011, true, false),
39 | UNKNOWN_PROTOCOL (4012, true, false),
40 | DISCONNECTED (4014, false, false),
41 | VOICE_SERVER_CRASHED (4015, false, true),
42 | UNKNOWN_ENCRYPTION_MODE (4016, true, false),
43 | //@formatter:on
44 | ;
45 |
46 | public static Optional parse(final int code) {
47 | for (final CloseCode closeCode : CloseCode.values()) {
48 | if (closeCode.code == code) {
49 | return Optional.of(closeCode);
50 | }
51 | }
52 | return Optional.empty();
53 | }
54 |
55 | private final int code;
56 | private final boolean shouldWarn;
57 | private final boolean shouldResume;
58 |
59 | CloseCode(final int code, final boolean shouldWarn, final boolean shouldResume) {
60 | this.code = code;
61 | this.shouldWarn = shouldWarn;
62 | this.shouldResume = shouldResume;
63 | }
64 |
65 | public int getCode() {
66 | return this.code;
67 | }
68 |
69 | public boolean shouldWarn() {
70 | return this.shouldWarn;
71 | }
72 |
73 | public boolean shouldResume() {
74 | return this.shouldResume;
75 | }
76 |
77 |
78 | @Override
79 | public String toString() {
80 | return "[" + this.code + " " + this.name() + "]";
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/OpCode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws;
18 |
19 | public final class OpCode {
20 |
21 | // https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#voice-opcodes
22 | public static final int IDENTIFY = 0;
23 | public static final int SELECT_PROTOCOL = 1;
24 | public static final int READY = 2;
25 | public static final int HEARTBEAT = 3;
26 | public static final int SESSION_DESCRIPTION = 4;
27 | public static final int SPEAKING = 5;
28 | public static final int HEARTBEAT_ACK = 6;
29 | public static final int RESUME = 7;
30 | public static final int HELLO = 8;
31 | public static final int RESUMED = 9;
32 | public static final int OP_12 = 12; //not documented, but we do receive it
33 | public static final int CLIENT_DISCONNECT = 13;
34 | public static final int OP_14 = 14; //not documented, but we do receive it
35 |
36 | // Custom codes
37 | public static final int WEBSOCKET_CLOSE = 9001;
38 |
39 | private OpCode() {}
40 | }
41 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/Speaking.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws;
18 |
19 | import org.immutables.value.Value;
20 | import org.json.JSONObject;
21 | import space.npstr.magma.impl.events.audio.ws.in.InboundWsEvent;
22 | import space.npstr.magma.impl.events.audio.ws.out.OutboundWsEvent;
23 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
24 |
25 | /**
26 | * Created by napster on 21.04.18.
27 | */
28 | @Value.Immutable
29 | @ImmutableWsEvent
30 | public abstract class Speaking implements InboundWsEvent, OutboundWsEvent {
31 |
32 | @Override
33 | public int getOpCode() {
34 | return OpCode.SPEAKING;
35 | }
36 |
37 | public abstract int getSpeakingMask();
38 |
39 | public abstract int getSsrc();
40 |
41 | @Override
42 | public Object getData() {
43 | return new JSONObject()
44 | .put("speaking", this.getSpeakingMask())
45 | .put("delay", 0)
46 | .put("ssrc", getSsrc())
47 | ;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/WsEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws;
18 |
19 | /**
20 | * Created by napster on 21.04.18.
21 | */
22 | public interface WsEvent {
23 |
24 | /**
25 | * @return the Op code of the event.
26 | */
27 | int getOpCode();
28 | }
29 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/ClientDisconnect.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.events.audio.ws.OpCode;
21 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
22 |
23 | /**
24 | * Created by napster on 21.04.18.
25 | */
26 | @Value.Immutable
27 | @ImmutableWsEvent
28 | public abstract class ClientDisconnect implements InboundWsEvent {
29 |
30 | @Override
31 | public int getOpCode() {
32 | return OpCode.CLIENT_DISCONNECT;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/HeartbeatAck.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.events.audio.ws.OpCode;
21 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
22 |
23 | /**
24 | * Created by napster on 21.04.18.
25 | */
26 | @Value.Immutable
27 | @ImmutableWsEvent
28 | public abstract class HeartbeatAck implements InboundWsEvent {
29 |
30 | @Override
31 | public int getOpCode() {
32 | return OpCode.HEARTBEAT_ACK;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/Hello.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.events.audio.ws.OpCode;
21 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
22 |
23 | /**
24 | * Created by napster on 20.04.18.
25 | *
26 | * Received the heartbeat interval
27 | */
28 | @Value.Immutable
29 | @ImmutableWsEvent
30 | public abstract class Hello implements InboundWsEvent {
31 |
32 | @Override
33 | public int getOpCode() {
34 | return OpCode.HELLO;
35 | }
36 |
37 | /**
38 | * @return The heartbeat interval that we received from Discord's Hello event (op 8)
39 | */
40 | public abstract int getHeartbeatIntervalMillis();
41 | }
42 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/Ignored.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
21 |
22 | /**
23 | * Created by napster on 24.04.18.
24 | */
25 | @Value.Immutable
26 | @ImmutableWsEvent
27 | public abstract class Ignored implements InboundWsEvent {
28 |
29 | @Override
30 | public abstract int getOpCode();
31 |
32 | public abstract String getPayload();
33 | }
34 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/InboundWsEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.json.JSONArray;
20 | import org.json.JSONObject;
21 | import space.npstr.magma.impl.EncryptionMode;
22 | import space.npstr.magma.impl.connections.AudioConnection;
23 | import space.npstr.magma.impl.events.audio.ws.OpCode;
24 | import space.npstr.magma.impl.events.audio.ws.SpeakingWsEvent;
25 | import space.npstr.magma.impl.events.audio.ws.WsEvent;
26 | import space.npstr.magma.impl.events.audio.ws.out.OutboundWsEvent;
27 |
28 | import java.util.Optional;
29 |
30 | /**
31 | * Created by napster on 20.04.18.
32 | *
33 | * Events that may be received from Discord.
34 | * Counterpart to {@link OutboundWsEvent}
35 | */
36 | public interface InboundWsEvent extends WsEvent {
37 |
38 | /**
39 | * This method may throw if Discord sends us bogus json data. This is not unlikely given Discord's history api. todo figure out error handling for it
40 | *
41 | * @param payload
42 | * the payload of the websocket message as a string
43 | *
44 | * @return a parsed {@link InboundWsEvent}
45 | */
46 | static InboundWsEvent from(final String payload) {
47 | final JSONObject content = new JSONObject(payload);
48 | final int opCode = content.getInt("op");
49 |
50 | switch (opCode) {
51 | case OpCode.HELLO:
52 | final JSONObject helloD = content.getJSONObject("d");
53 | return HelloWsEvent.builder()
54 | .heartbeatIntervalMillis(helloD.getInt("heartbeat_interval"))
55 | .build();
56 | case OpCode.READY:
57 | final JSONObject readyD = content.getJSONObject("d");
58 | return ReadyWsEvent.builder()
59 | .ssrc(readyD.getInt("ssrc"))
60 | .ip(readyD.getString("ip"))
61 | .port(readyD.getInt("port"))
62 | .addAllEncryptionModes(EncryptionMode.fromJson(readyD.getJSONArray("modes")))
63 | .build();
64 | case OpCode.SESSION_DESCRIPTION:
65 | final JSONObject sessionD = content.getJSONObject("d");
66 | final String mode = sessionD.getString("mode");
67 | final Optional encryptionMode = EncryptionMode.parse(mode);
68 | if (!encryptionMode.isPresent()) {
69 | throw new RuntimeException("No / unknown encryption mode: " + mode); //todo how are exceptions handled? ensure json payload is logged
70 | }
71 | final JSONArray keyArray = sessionD.getJSONArray("secret_key");
72 | final byte[] secretKey = new byte[AudioConnection.DISCORD_SECRET_KEY_LENGTH];
73 | for (int i = 0; i < keyArray.length(); i++) {
74 | secretKey[i] = (byte) keyArray.getInt(i);
75 | }
76 |
77 | return SessionDescriptionWsEvent.builder()
78 | .encryptionMode(encryptionMode.get())
79 | .secretKey(secretKey)
80 | .build();
81 | case OpCode.SPEAKING:
82 | final JSONObject speakingD = content.getJSONObject("d");
83 | return SpeakingWsEvent.builder()
84 | .speakingMask(speakingD.getInt("speaking"))
85 | .ssrc(speakingD.getInt("ssrc"))
86 | .build();
87 | case OpCode.HEARTBEAT_ACK:
88 | return HeartbeatAckWsEvent.builder()
89 | .build();
90 | case OpCode.RESUMED:
91 | return ResumedWsEvent.builder()
92 | .build();
93 | case OpCode.OP_12:
94 | case OpCode.OP_14:
95 | return IgnoredWsEvent.builder()
96 | .opCode(opCode)
97 | .payload(payload)
98 | .build();
99 | case OpCode.CLIENT_DISCONNECT:
100 | return ClientDisconnectWsEvent.builder()
101 | .build();
102 | case OpCode.WEBSOCKET_CLOSE:
103 | final JSONObject closedD = content.getJSONObject("d");
104 | return WebSocketClosedWsEvent.builder()
105 | .code(closedD.getInt("code"))
106 | .reason(closedD.getString("reason"))
107 | .build();
108 | default:
109 | return UnknownWsEvent.builder()
110 | .opCode(opCode)
111 | .payload(payload)
112 | .build();
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/Ready.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.EncryptionMode;
21 | import space.npstr.magma.impl.events.audio.ws.OpCode;
22 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
23 |
24 | import java.util.List;
25 |
26 | /**
27 | * Created by napster on 21.04.18.
28 | */
29 | @Value.Immutable
30 | @ImmutableWsEvent
31 | public abstract class Ready implements InboundWsEvent {
32 |
33 | @Override
34 | public int getOpCode() {
35 | return OpCode.READY;
36 | }
37 |
38 | /**
39 | * @return our ssrc
40 | */
41 | public abstract int getSsrc();
42 |
43 | /**
44 | * @return the ip address that we should connect our udp connection to
45 | */
46 | public abstract String getIp();
47 |
48 | /**
49 | * @return the udp port that we should connect our udp connection to
50 | */
51 | public abstract int getPort();
52 |
53 | /**
54 | * @return the supported encryption modes
55 | */
56 | public abstract List getEncryptionModes();
57 | }
58 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/Resumed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.events.audio.ws.OpCode;
21 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
22 |
23 | /**
24 | * Created by napster on 21.04.18.
25 | */
26 | @Value.Immutable
27 | @ImmutableWsEvent
28 | public abstract class Resumed implements InboundWsEvent {
29 |
30 | @Override
31 | public int getOpCode() {
32 | return OpCode.RESUMED;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/SessionDescription.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.EncryptionMode;
21 | import space.npstr.magma.impl.events.audio.ws.OpCode;
22 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
23 |
24 | /**
25 | * Created by napster on 20.04.18.
26 | */
27 | @Value.Immutable
28 | @ImmutableWsEvent
29 | public abstract class SessionDescription implements InboundWsEvent {
30 |
31 | @Override
32 | public int getOpCode() {
33 | return OpCode.SESSION_DESCRIPTION;
34 | }
35 |
36 | /**
37 | * @return the encryption mode of this session
38 | */
39 | public abstract EncryptionMode getEncryptionMode();
40 |
41 | /**
42 | * @return the secret key sent to us by the Session Description voice websocket event (op 4)
43 | */
44 | public abstract byte[] getSecretKey();
45 | }
46 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/Unknown.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
21 |
22 | /**
23 | * Created by napster on 20.04.18.
24 | *
25 | * Unknown event received
26 | */
27 | @Value.Immutable
28 | @ImmutableWsEvent
29 | public abstract class Unknown implements InboundWsEvent {
30 |
31 | public abstract String getPayload();
32 | }
33 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/WebSocketClosed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.in;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.events.audio.ws.OpCode;
21 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
22 |
23 | /**
24 | * Created by napster on 22.04.18.
25 | */
26 | @Value.Immutable
27 | @ImmutableWsEvent
28 | public abstract class WebSocketClosed implements InboundWsEvent {
29 |
30 | @Override
31 | public int getOpCode() {
32 | return OpCode.WEBSOCKET_CLOSE;
33 | }
34 |
35 | public abstract int getCode();
36 |
37 | public abstract String getReason();
38 | }
39 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/in/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.events.audio.ws.in;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/out/Heartbeat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.out;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.events.audio.ws.OpCode;
21 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
22 |
23 | /**
24 | * Created by napster on 21.04.18.
25 | */
26 | @Value.Immutable
27 | @ImmutableWsEvent
28 | public abstract class Heartbeat implements OutboundWsEvent {
29 |
30 | public abstract int getNonce();
31 |
32 | @Override
33 | public int getOpCode() {
34 | return OpCode.HEARTBEAT;
35 | }
36 |
37 | @Override
38 | public Integer getData() {
39 | return this.getNonce();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/out/Identify.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.out;
18 |
19 | import org.immutables.value.Value;
20 | import org.json.JSONObject;
21 | import space.npstr.magma.impl.events.audio.ws.OpCode;
22 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
23 |
24 | /**
25 | * Created by napster on 21.04.18.
26 | */
27 | @Value.Immutable
28 | @ImmutableWsEvent
29 | public abstract class Identify implements OutboundWsEvent {
30 |
31 | public abstract String getUserId();
32 |
33 | public abstract String getGuildId();
34 |
35 | public abstract String getSessionId();
36 |
37 | public abstract String getToken();
38 |
39 |
40 | @Override
41 | public int getOpCode() {
42 | return OpCode.IDENTIFY;
43 | }
44 |
45 | @Override
46 | public JSONObject getData() {
47 | return new JSONObject()
48 | .put("server_id", this.getGuildId())
49 | .put("user_id", this.getUserId())
50 | .put("session_id", this.getSessionId())
51 | .put("token", this.getToken());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/out/OutboundWsEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.out;
18 |
19 | import org.json.JSONObject;
20 | import space.npstr.magma.impl.events.audio.ws.WsEvent;
21 | import space.npstr.magma.impl.events.audio.ws.in.InboundWsEvent;
22 |
23 | /**
24 | * Created by napster on 21.04.18.
25 | *
26 | * Payloads that we may send to Discord.
27 | * Counterpart to {@link InboundWsEvent}
28 | */
29 | public interface OutboundWsEvent extends WsEvent {
30 |
31 | /**
32 | * @return Data payload
33 | * Should be an object that json understands and correctly parses {@literal ->} strings get double quoted for example
34 | */
35 | Object getData();
36 |
37 | /**
38 | * Build a message that can be send to Discord over the websocket.
39 | */
40 | default String asMessage() {
41 | return new JSONObject()
42 | .put("op", this.getOpCode())
43 | .put("d", this.getData())
44 | .toString();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/out/Resume.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.out;
18 |
19 | import org.immutables.value.Value;
20 | import org.json.JSONObject;
21 | import space.npstr.magma.impl.events.audio.ws.OpCode;
22 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
23 |
24 | /**
25 | * Created by napster on 25.04.18.
26 | */
27 | @Value.Immutable
28 | @ImmutableWsEvent
29 | public abstract class Resume implements OutboundWsEvent {
30 |
31 | public abstract String getGuildId();
32 |
33 | public abstract String getSessionId();
34 |
35 | public abstract String getToken();
36 |
37 | @Override
38 | public int getOpCode() {
39 | return OpCode.RESUME;
40 | }
41 |
42 | @Override
43 | public JSONObject getData() {
44 | return new JSONObject()
45 | .put("server_id", this.getGuildId())
46 | .put("session_id", this.getSessionId())
47 | .put("token", this.getToken());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/out/SelectProtocol.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.events.audio.ws.out;
18 |
19 | import org.immutables.value.Value;
20 | import org.json.JSONObject;
21 | import space.npstr.magma.impl.EncryptionMode;
22 | import space.npstr.magma.impl.events.audio.ws.OpCode;
23 | import space.npstr.magma.impl.immutables.ImmutableWsEvent;
24 |
25 | /**
26 | * Created by napster on 21.04.18.
27 | */
28 | @Value.Immutable
29 | @ImmutableWsEvent
30 | public abstract class SelectProtocol implements OutboundWsEvent {
31 |
32 | public abstract String getProtocol();
33 |
34 | public abstract String getHost();
35 |
36 | public abstract int getPort();
37 |
38 | public abstract EncryptionMode getEncryptionMode();
39 |
40 | @Override
41 | public int getOpCode() {
42 | return OpCode.SELECT_PROTOCOL;
43 | }
44 |
45 | @Override
46 | public JSONObject getData() {
47 | return new JSONObject()
48 | .put("protocol", this.getProtocol())
49 | .put("data", new JSONObject()
50 | .put("address", this.getHost())
51 | .put("port", this.getPort())
52 | .put("mode", this.getEncryptionMode().getKey()));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/out/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.events.audio.ws.out;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/audio/ws/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.events.audio.ws;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/events/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.events;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/immutables/ImmutableLcEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.immutables;
18 |
19 | import org.immutables.value.Value;
20 |
21 | /**
22 | * Created by napster on 21.04.18.
23 | */
24 | @Value.Style(
25 | typeImmutable = "*LcEvent",
26 | stagedBuilder = true
27 | )
28 | public @interface ImmutableLcEvent {
29 | }
30 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/immutables/ImmutableWsEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.immutables;
18 |
19 | import org.immutables.value.Value;
20 |
21 | /**
22 | * Created by napster on 21.04.18.
23 | */
24 | @Value.Style(
25 | typeImmutable = "*WsEvent",
26 | stagedBuilder = true
27 | )
28 | public @interface ImmutableWsEvent {
29 | }
30 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/immutables/SessionInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.immutables;
18 |
19 | import org.immutables.value.Value;
20 | import space.npstr.magma.impl.events.audio.lifecycle.VoiceServerUpdate;
21 |
22 | /**
23 | * Created by napster on 20.04.18.
24 | */
25 | @Value.Immutable
26 | @Value.Style(stagedBuilder = true)
27 | public abstract class SessionInfo {
28 |
29 | public abstract VoiceServerUpdate getVoiceServerUpdate();
30 |
31 | public String getUserId() {
32 | return this.getVoiceServerUpdate().getUserId();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/immutables/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.immutables;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/processing/PacketProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.processing;
18 |
19 | import com.iwebpp.crypto.TweetNaclFast;
20 | import edu.umd.cs.findbugs.annotations.Nullable;
21 | import net.dv8tion.jda.api.audio.AudioPacket;
22 | import net.dv8tion.jda.api.audio.AudioSendHandler;
23 | import net.dv8tion.jda.api.audio.factory.IPacketProvider;
24 | import net.dv8tion.jda.api.audio.hooks.ConnectionStatus;
25 | import org.slf4j.Logger;
26 | import org.slf4j.LoggerFactory;
27 | import space.npstr.magma.impl.EncryptionMode;
28 | import space.npstr.magma.impl.connections.AudioConnection;
29 |
30 | import java.net.DatagramPacket;
31 | import java.net.DatagramSocket;
32 | import java.net.InetSocketAddress;
33 | import java.nio.ByteBuffer;
34 | import java.util.function.LongSupplier;
35 |
36 | /**
37 | * Created by napster on 23.06.18.
38 | */
39 | public class PacketProvider implements IPacketProvider {
40 |
41 | private static final Logger log = LoggerFactory.getLogger(PacketProvider.class);
42 | private static final String INFORMATION_NOT_AVAILABLE = "This information is not available";
43 | private static final ByteBuffer SILENCE_BYTES = ByteBuffer.wrap(new byte[]{(byte) 0xF8, (byte) 0xFF, (byte) 0xFE});
44 | private static final int EMPTY_FRAMES_COUNT = 5;
45 |
46 | private final AudioConnection audioConnection;
47 | private final LongSupplier nonceSupplier;
48 | private ByteBuffer packetBuffer = ByteBuffer.allocate(512); //packets usually take up about 400-500 bytes
49 | private final byte[] nonceBuffer = new byte[TweetNaclFast.SecretBox.nonceLength];
50 |
51 | private char seq = 0; //Sequence of audio packets. Used to determine the order of the packets.
52 | private int timestamp = 0; //Used to sync up our packets within the same timeframe of other people talking.
53 |
54 | // opus interpolation handling
55 | // https://discordapp.com/developers/docs/topics/voice-connections#voice-data-interpolation
56 | private int sendSilentFrames = EMPTY_FRAMES_COUNT;
57 |
58 | public PacketProvider(final AudioConnection audioConnection, final LongSupplier nonceSupplier) {
59 | this.audioConnection = audioConnection;
60 | this.nonceSupplier = nonceSupplier;
61 | }
62 |
63 | @Override
64 | public String getIdentifier() {
65 | return "";
66 | }
67 |
68 | @Override
69 | public String getConnectedChannel() {
70 | throw new UnsupportedOperationException(INFORMATION_NOT_AVAILABLE);
71 | }
72 |
73 | @Override
74 | public DatagramSocket getUdpSocket() {
75 | return this.audioConnection.getUdpSocket();
76 | }
77 |
78 | @Nullable
79 | @Override
80 | public InetSocketAddress getSocketAddress() {
81 | return this.audioConnection.getUdpTargetAddress();
82 | }
83 |
84 | @Override
85 | public void onConnectionError(final ConnectionStatus status) {
86 | // this is not for UDP you smartass
87 | throw new UnsupportedOperationException("Connection error, on a udp connection...that's not a real thing.");
88 | }
89 |
90 | @Override
91 | public void onConnectionLost() {
92 | // this is not for UDP you smartass
93 | throw new UnsupportedOperationException("Connection lost, on a udp connection...that's not a real thing.");
94 | }
95 |
96 | //realistically, this is the only thing that is ever called by the NativeAudioSystem
97 | @Nullable
98 | @Override
99 | public DatagramPacket getNextPacket(final boolean changeTalking) {
100 | final InetSocketAddress targetAddress = this.audioConnection.getUdpTargetAddress();
101 | if (targetAddress == null) {
102 | return null;
103 | }
104 | final ByteBuffer nextPacket = getNextPacketRaw(changeTalking);
105 | return nextPacket == null ? null : asDatagramPacket(nextPacket, targetAddress);
106 | }
107 |
108 | @Nullable
109 | @Override
110 | public ByteBuffer getNextPacketRaw(final boolean changeTalking) {
111 | try {
112 | return this.buildNextPacket(changeTalking);
113 | } catch (final Exception e) {
114 | log.error("Failed to get next packet", e);
115 | return null;
116 | }
117 | }
118 |
119 | @Nullable
120 | @SuppressWarnings("squid:S3776")
121 | private ByteBuffer buildNextPacket(final boolean changeTalking) {
122 |
123 | final EncryptionMode encryptionMode = this.audioConnection.getEncryptionMode();
124 | final byte[] secretKey = this.audioConnection.getSecretKey();
125 | final Integer ssrc = this.audioConnection.getSsrc();
126 | final AudioSendHandler sendHandler = this.audioConnection.getSendHandler();
127 |
128 | //preconditions fulfilled?
129 | if (encryptionMode == null
130 | || secretKey == null
131 | || ssrc == null
132 | || sendHandler == null
133 | || !sendHandler.canProvide()) {
134 | if (this.audioConnection.isSpeaking() && changeTalking) {
135 | this.audioConnection.updateSpeaking(false);
136 | }
137 | this.sendSilentFrames = EMPTY_FRAMES_COUNT;
138 | return null;
139 | }
140 |
141 | final AudioPacket nextAudioPacket;
142 | if (this.sendSilentFrames <= 0) {
143 | //audio data provided?
144 | final ByteBuffer rawAudio = sendHandler.provide20MsAudio();
145 | if (rawAudio == null || !rawAudio.hasRemaining()) {
146 | if (this.audioConnection.isSpeaking() && changeTalking) {
147 | this.audioConnection.updateSpeaking(false);
148 | }
149 | this.sendSilentFrames = EMPTY_FRAMES_COUNT;
150 | return null;
151 | }
152 | if (!rawAudio.hasArray()) {
153 | // we can't use the boxer without an array so encryption would not work
154 | log.error("AudioSendHandler provided ByteBuffer without a backing array! This is unsupported.");
155 | return null;
156 | }
157 | nextAudioPacket = new AudioPacket(this.seq, this.timestamp, ssrc, rawAudio);
158 | } else {
159 | nextAudioPacket = new AudioPacket(this.seq, this.timestamp, ssrc, SILENCE_BYTES);
160 | this.sendSilentFrames--;
161 | log.trace("Sending silent frame, silent frames left {}", this.sendSilentFrames);
162 | }
163 |
164 | final ByteBuffer nextPacket = this.packetBuffer = PacketUtil.encryptPacket(nextAudioPacket, this.packetBuffer,
165 | encryptionMode, secretKey, this.nonceSupplier, this.nonceBuffer);
166 |
167 | if (!this.audioConnection.isSpeaking()) {
168 | this.audioConnection.updateSpeaking(true);
169 | }
170 |
171 | if (this.seq + 1 > Character.MAX_VALUE) {
172 | this.seq = 0;
173 | } else {
174 | this.seq++;
175 | }
176 |
177 | this.timestamp += AudioConnection.OPUS_FRAME_SIZE;
178 |
179 | return nextPacket;
180 | }
181 |
182 | private DatagramPacket asDatagramPacket(final ByteBuffer buffer, final InetSocketAddress targetAddress) {
183 | final byte[] data = buffer.array();
184 | final int offset = buffer.arrayOffset();
185 | final int limit = buffer.remaining();
186 | return new DatagramPacket(data, offset, limit, targetAddress);
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/processing/PacketUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl.processing;
18 |
19 | import com.iwebpp.crypto.TweetNaclFast;
20 | import net.dv8tion.jda.api.audio.AudioPacket;
21 | import space.npstr.magma.impl.EncryptionMode;
22 |
23 | import java.nio.ByteBuffer;
24 | import java.util.concurrent.ThreadLocalRandom;
25 | import java.util.function.LongSupplier;
26 |
27 | /**
28 | * Created by napster on 23.06.18.
29 | */
30 | public class PacketUtil {
31 |
32 | private PacketUtil() {
33 | }
34 |
35 | //this may reallocate the passed ByteBuffer if it is too small
36 | public static ByteBuffer encryptPacket(final AudioPacket audioPacket, final ByteBuffer packetBuffer,
37 | final EncryptionMode encryptionMode, final byte[] secretKey,
38 | final LongSupplier nonceSupplier, final byte[] nonceBuffer) {
39 |
40 | final int nonceLength;
41 | switch (encryptionMode) {
42 | case XSALSA20_POLY1305:
43 | nonceLength = 0;
44 | break;
45 | case XSALSA20_POLY1305_LITE:
46 | writeNonce(nonceSupplier.getAsLong(), nonceBuffer);
47 | nonceLength = 4;
48 | break;
49 | case XSALSA20_POLY1305_SUFFIX:
50 | ThreadLocalRandom.current().nextBytes(nonceBuffer);
51 | nonceLength = TweetNaclFast.SecretBox.nonceLength;
52 | break;
53 | default:
54 | throw new IllegalStateException("Encryption mode [" + encryptionMode + "] is not supported!");
55 | }
56 |
57 | return audioPacket.asEncryptedPacket(packetBuffer, secretKey, nonceBuffer, nonceLength);
58 | }
59 |
60 | //@formatter:off
61 | public static void writeNonce(final long nonce, final byte[] nonceBuffer) {
62 | nonceBuffer[0] = (byte) ((nonce >>> 24) & 0xFF);
63 | nonceBuffer[1] = (byte) ((nonce >>> 16) & 0xFF);
64 | nonceBuffer[2] = (byte) ((nonce >>> 8) & 0xFF);
65 | nonceBuffer[3] = (byte) ( nonce & 0xFF);
66 | }
67 | //@formatter:on
68 | }
69 |
--------------------------------------------------------------------------------
/impl/src/main/java/space/npstr/magma/impl/processing/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @FieldsAreNonNullByDefault
18 | @ParametersAreNonnullByDefault
19 | @ReturnTypesAreNonNullByDefault
20 | package space.npstr.magma.impl.processing;
21 |
22 | import space.npstr.annotations.FieldsAreNonNullByDefault;
23 | import space.npstr.annotations.ParametersAreNonnullByDefault;
24 | import space.npstr.annotations.ReturnTypesAreNonNullByDefault;
25 |
--------------------------------------------------------------------------------
/impl/src/test/java/space/npstr/magma/impl/EncryptionModeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma.impl;
18 |
19 | import org.json.JSONArray;
20 | import org.junit.jupiter.api.Test;
21 |
22 | import java.util.Collections;
23 | import java.util.List;
24 | import java.util.Optional;
25 |
26 | import static org.junit.jupiter.api.Assertions.assertEquals;
27 | import static org.junit.jupiter.api.Assertions.assertFalse;
28 | import static org.junit.jupiter.api.Assertions.assertTrue;
29 |
30 | /**
31 | * Created by napster on 07.05.18.
32 | */
33 | public class EncryptionModeTest {
34 |
35 | @Test
36 | public void testPreference() {
37 | JSONArray array = new JSONArray();
38 | array.put("xsalsa20_poly1305_lite");
39 | array.put("xsalsa20_poly1305");
40 | array.put("xsalsa20_poly1305_suffix");
41 | final List allModes = EncryptionMode.fromJson(array);
42 | assertEquals(allModes.size(), array.length(), "all known modes were parsed");
43 |
44 | final Optional preferredMode = EncryptionMode.getPreferredMode(allModes);
45 | assertTrue(preferredMode.isPresent(), "return a preferred mode");
46 | assertEquals(EncryptionMode.XSALSA20_POLY1305_LITE, preferredMode.get(), "prefer lite over all others");
47 |
48 |
49 | final List empty = Collections.emptyList();
50 | assertFalse(EncryptionMode.getPreferredMode(empty).isPresent(), "empty list returns empty optional");
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk8
3 |
--------------------------------------------------------------------------------
/platform/.gitignore:
--------------------------------------------------------------------------------
1 | build/
--------------------------------------------------------------------------------
/platform/gradle/dependency-locks/archives.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/platform/gradle/dependency-locks/classpath.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.springframework.boot:spring-boot-dependencies:2.2.6.RELEASE
5 |
--------------------------------------------------------------------------------
/platform/gradle/dependency-locks/default.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 |
--------------------------------------------------------------------------------
/platform/gradle/dependency-locks/jacocoAgent.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 |
--------------------------------------------------------------------------------
/platform/gradle/dependency-locks/jacocoAnt.lockfile:
--------------------------------------------------------------------------------
1 | # This is a Gradle generated file for dependency locking.
2 | # Manual edits can break the build and are not advised.
3 | # This file is expected to be part of source control.
4 | org.jacoco:org.jacoco.agent:0.8.5
5 | org.jacoco:org.jacoco.ant:0.8.5
6 | org.jacoco:org.jacoco.core:0.8.5
7 | org.jacoco:org.jacoco.report:0.8.5
8 | org.ow2.asm:asm-analysis:7.2
9 | org.ow2.asm:asm-commons:7.2
10 | org.ow2.asm:asm-tree:7.2
11 | org.ow2.asm:asm:7.2
12 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'magma'
2 |
3 | include 'api'
4 | include 'impl'
5 | include 'platform'
6 |
--------------------------------------------------------------------------------
/src/main/java/space/npstr/magma/MagmaFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 Dennis Neufeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package space.npstr.magma;
18 |
19 | import java.util.function.Function;
20 | import net.dv8tion.jda.api.audio.factory.IAudioSendFactory;
21 | import space.npstr.magma.api.MagmaApi;
22 | import space.npstr.magma.api.Member;
23 | import space.npstr.magma.impl.Magma;
24 |
25 | /**
26 | * Created by napster on 08.05.19.
27 | */
28 | public class MagmaFactory {
29 |
30 | /**
31 | * Create a new Magma instance. More than one of these is not necessary, even if you are managing several shards and
32 | * several bot accounts. A single instance of this scales automatically according to your needs and hardware.
33 | *
34 | * @param sendFactoryProvider a provider of {@link IAudioSendFactory}s. It will have members applied to it.
35 | */
36 | public static MagmaApi of(final Function sendFactoryProvider) {
37 | return new Magma(sendFactoryProvider);
38 | }
39 |
40 | private MagmaFactory() {}
41 | }
42 |
--------------------------------------------------------------------------------