@@ -0,0 +1,3 @@ | |||
/src/main/resources/application.properties | |||
.idea | |||
/target/ |
@@ -0,0 +1,2 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<module type="JAVA_MODULE" version="4" /> |
@@ -23,11 +23,20 @@ | |||
<version>3.8.1</version> | |||
<configuration> | |||
<release>11</release> | |||
<source>11</source> | |||
<target>11</target> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
<repositories> | |||
<repository> | |||
<id>jitpack.io</id> | |||
<url>https://jitpack.io</url> | |||
</repository> | |||
</repositories> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
@@ -49,15 +58,6 @@ | |||
<groupId>org.postgresql</groupId> | |||
<artifactId>postgresql</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.security</groupId> | |||
<artifactId>spring-security-core</artifactId> | |||
<version>5.1.6.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-security</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
@@ -67,6 +67,16 @@ | |||
<artifactId>discord4j-core</artifactId> | |||
<version>3.0.12</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.github.HayateLaTech</groupId> | |||
<artifactId>OAuth2Discord</artifactId> | |||
<version>master-SNAPSHOT</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-configuration-processor</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
</dependencies> | |||
@@ -0,0 +1,75 @@ | |||
package io.github.norbipeti.gcdc.controller; | |||
import bell.oauth.discord.main.OAuthBuilder; | |||
import bell.oauth.discord.main.Response; | |||
import discord4j.core.object.entity.Message; | |||
import discord4j.core.object.util.Snowflake; | |||
import io.github.norbipeti.gcdc.model.Session; | |||
import io.github.norbipeti.gcdc.service.DiscordService; | |||
import io.github.norbipeti.gcdc.service.SessionService; | |||
import lombok.RequiredArgsConstructor; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.http.HttpStatus; | |||
import org.springframework.web.bind.annotation.*; | |||
import org.springframework.web.context.request.async.DeferredResult; | |||
import org.springframework.web.server.ResponseStatusException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.validation.Valid; | |||
import java.security.Principal; | |||
import java.util.Collections; | |||
import java.util.Map; | |||
import java.util.UUID; | |||
@RestController | |||
@RequiredArgsConstructor | |||
public class DCController { | |||
private static final Map<String, String> RESULT_OK = Collections.singletonMap("response", "OK"); | |||
private final SessionService service; | |||
private final DiscordService discordService; | |||
@Value("${discord.secret}") | |||
private String secret; | |||
private final OAuthBuilder builder = new OAuthBuilder("680138144812892371", secret); | |||
@GetMapping("/api/users/register") | |||
public Map<String, String> register(@RequestParam String state, @RequestParam String code, HttpServletRequest request) { | |||
if (state == null || code == null) throw new ResponseStatusException(HttpStatus.BAD_REQUEST); | |||
long channel; | |||
try { channel = Long.parseLong(state); } catch (NumberFormatException e) { | |||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST); | |||
} | |||
builder.setRedirectURI(request.getRequestURL().toString()); | |||
if (builder.exchange(code) == Response.ERROR) | |||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST); | |||
long user = Snowflake.of(builder.getUser().getId()).asLong(); | |||
service.deleteSession(user); | |||
service.insertSession(new Session(UUID.randomUUID().toString(), Snowflake.of(channel), user)); | |||
return RESULT_OK; | |||
} | |||
@GetMapping("/api/users/get") | |||
public Map<String, String> getUser(@RequestParam String token) { | |||
getSession(token); //Returns unauthorized if needed | |||
return RESULT_OK; | |||
} | |||
@PostMapping("/api/messages/send") | |||
public Map<String, String> sendMessage(@RequestParam String token, @RequestParam String message) { | |||
var sess = getSession(token); | |||
discordService.sendMessage(sess.getChannel().asLong(), sess.getUser(), message); | |||
return RESULT_OK; | |||
} | |||
@GetMapping("/api/messages/get") | |||
public DeferredResult<Map<String, String>> getMessage(@RequestParam String token) { | |||
var sess = getSession(token); | |||
return discordService.getMessage(sess.getChannel().asLong()); | |||
} | |||
private Session getSession(String token) { | |||
var sess = service.getSession(token); | |||
if (sess == null) | |||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); | |||
return sess; | |||
} | |||
} |
@@ -9,4 +9,5 @@ import lombok.Data; | |||
public class Session { | |||
String token; | |||
Snowflake channel; | |||
long user; | |||
} |
@@ -0,0 +1,61 @@ | |||
package io.github.norbipeti.gcdc.service; | |||
import discord4j.core.DiscordClient; | |||
import discord4j.core.DiscordClientBuilder; | |||
import discord4j.core.event.domain.message.MessageCreateEvent; | |||
import discord4j.core.object.entity.GuildMessageChannel; | |||
import discord4j.core.object.entity.Member; | |||
import discord4j.core.object.entity.Message; | |||
import discord4j.core.object.entity.MessageChannel; | |||
import discord4j.core.object.util.Snowflake; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.stereotype.Component; | |||
import org.springframework.web.context.request.async.DeferredResult; | |||
import reactor.core.publisher.Mono; | |||
import javax.annotation.PostConstruct; | |||
import java.awt.*; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
@Component | |||
public class DiscordService { | |||
private DiscordClient client; | |||
@Value("${discord.token}") | |||
private String token; | |||
@PostConstruct | |||
public void init() { | |||
this.client = new DiscordClientBuilder(token).build(); | |||
client.getEventDispatcher().on(MessageCreateEvent.class).subscribe(event -> { | |||
if (event.getMember().isEmpty() || event.getMessage().getContent().isEmpty()) | |||
return; | |||
var req = requests.remove(event.getMessage().getChannelId().asLong()); | |||
if (req == null) return; | |||
var msg = event.getMember().map(member -> Map.of("username", member.getDisplayName(), | |||
"message", event.getMessage().getContent().orElse(""))); | |||
msg.ifPresent(req::setResult); | |||
}); | |||
client.login().subscribe(); | |||
} | |||
public void sendMessage(long channel, long user, String message) { | |||
client.getChannelById(Snowflake.of(channel)).cast(GuildMessageChannel.class) | |||
.flatMap(ch -> getUsername(user, ch) | |||
.flatMap(name -> ch.createEmbed(ecs -> ecs.setAuthor(name, null, null) | |||
.setColor(Color.BLUE).setDescription(message)))).subscribe(); | |||
} | |||
private final HashMap<Long, DeferredResult<Map<String, String>>> requests = new HashMap<>(); | |||
public DeferredResult<Map<String, String>> getMessage(long channel) { | |||
var result = new DeferredResult<Map<String, String>>(120000L); | |||
requests.put(channel, result); | |||
return result; | |||
} | |||
private Mono<String> getUsername(long user, GuildMessageChannel channel) { | |||
return client.getUserById(Snowflake.of(user)).flatMap(u -> u.asMember(channel.getGuildId())) | |||
.map(Member::getDisplayName); | |||
} | |||
} |
@@ -29,18 +29,29 @@ public class SessionService extends JdbcDaoSupport { | |||
public Session getSession(String token) { | |||
String sql = "SELECT * FROM sessions WHERE token=? LIMIT 1"; | |||
List<Map<String, Object>> rows = getJdbcTemplate().queryForList(sql, token); | |||
return getSession(getJdbcTemplate().queryForList(sql, token)); | |||
} | |||
public Session getSession(long user) { | |||
String sql = "SELECT * FROM sessions WHERE user=? LIMIT 1"; | |||
return getSession(getJdbcTemplate().queryForList(sql, user)); | |||
} | |||
private Session getSession(List<Map<String, Object>> rows) { | |||
if (rows.size() == 0) return null; | |||
var row = rows.get(0); | |||
return new Session((String) row.get("token"), Snowflake.of((long) row.get("channel"))); | |||
return new Session((String) row.get("token"), Snowflake.of((long) row.get("channel")), | |||
(Long) row.get("user")); | |||
} | |||
public void deleteSession(String token) { | |||
String sql = "DELETE FROM sessions WHERE token=?"; | |||
getJdbcTemplate().update(sql, token); | |||
} | |||
public void deleteSession(long user) { | |||
String sql = "DELETE FROM sessions WHERE user=?"; | |||
getJdbcTemplate().update(sql, user); | |||
} | |||
public void updateSession(Session session) { | |||
@@ -0,0 +1,13 @@ | |||
{ | |||
"properties": [ | |||
{ | |||
"name": "discord.secret", | |||
"type": "java.lang.String", | |||
"description": "Description for discord.secret." | |||
}, | |||
{ | |||
"name": "discord.token", | |||
"type": "java.lang.String", | |||
"description": "Description for discord.token." | |||
} | |||
] } |