diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..31fe120 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/src/main/resources/application.properties +.idea +/target/ diff --git a/GCDC-Server.iml b/GCDC-Server.iml new file mode 100644 index 0000000..78b2cc5 --- /dev/null +++ b/GCDC-Server.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index f2d3b09..8624917 100644 --- a/pom.xml +++ b/pom.xml @@ -23,11 +23,20 @@ 3.8.1 11 + 11 + 11 + + + jitpack.io + https://jitpack.io + + + org.springframework.boot @@ -49,15 +58,6 @@ org.postgresql postgresql - - org.springframework.security - spring-security-core - 5.1.6.RELEASE - - - org.springframework.boot - spring-boot-starter-security - org.projectlombok lombok @@ -67,6 +67,16 @@ discord4j-core 3.0.12 + + com.github.HayateLaTech + OAuth2Discord + master-SNAPSHOT + + + org.springframework.boot + spring-boot-configuration-processor + true + diff --git a/src/main/java/io/github/norbipeti/gcdc/controller/DCController.java b/src/main/java/io/github/norbipeti/gcdc/controller/DCController.java new file mode 100644 index 0000000..a3c8c69 --- /dev/null +++ b/src/main/java/io/github/norbipeti/gcdc/controller/DCController.java @@ -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 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 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 getUser(@RequestParam String token) { + getSession(token); //Returns unauthorized if needed + return RESULT_OK; + } + + @PostMapping("/api/messages/send") + public Map 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> 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; + } +} diff --git a/src/main/java/io/github/norbipeti/gcdc/model/Session.java b/src/main/java/io/github/norbipeti/gcdc/model/Session.java index 889d909..b4324ff 100644 --- a/src/main/java/io/github/norbipeti/gcdc/model/Session.java +++ b/src/main/java/io/github/norbipeti/gcdc/model/Session.java @@ -9,4 +9,5 @@ import lombok.Data; public class Session { String token; Snowflake channel; + long user; } diff --git a/src/main/java/io/github/norbipeti/gcdc/service/DiscordService.java b/src/main/java/io/github/norbipeti/gcdc/service/DiscordService.java new file mode 100644 index 0000000..f75158d --- /dev/null +++ b/src/main/java/io/github/norbipeti/gcdc/service/DiscordService.java @@ -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>> requests = new HashMap<>(); + + public DeferredResult> getMessage(long channel) { + var result = new DeferredResult>(120000L); + requests.put(channel, result); + return result; + } + + private Mono getUsername(long user, GuildMessageChannel channel) { + return client.getUserById(Snowflake.of(user)).flatMap(u -> u.asMember(channel.getGuildId())) + .map(Member::getDisplayName); + } +} diff --git a/src/main/java/io/github/norbipeti/gcdc/service/SessionService.java b/src/main/java/io/github/norbipeti/gcdc/service/SessionService.java index a00871b..76034c3 100644 --- a/src/main/java/io/github/norbipeti/gcdc/service/SessionService.java +++ b/src/main/java/io/github/norbipeti/gcdc/service/SessionService.java @@ -29,18 +29,29 @@ public class SessionService extends JdbcDaoSupport { public Session getSession(String token) { String sql = "SELECT * FROM sessions WHERE token=? LIMIT 1"; - List> 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> 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) { diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..367b9d4 --- /dev/null +++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -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." + } + ] } \ No newline at end of file