当前位置:   article > 正文

Discord OAuth2授权以及机器人监听群事件

Discord OAuth2授权以及机器人监听群事件

下面文章讲解获取OAuth2授权整个流程,创建机器人,使用机器人监听工会(工会就是创建的服务器)成员变化等等,对接国外的都是需要VPN的哦,对接的时候记得提前准备。

创建应用

点击 此页面添加应用,,创建完成以后会生成应用名称,公钥,客户id等等,这些我们可以保存下来。这些应用会自动和我们的工会关联的。

创建机器人

在下面页面创建一个机器人,机器人会会生成自己的token,这个一定要保存好,注意机器人安全。


配置OAuth2

页面创建我们的OAuth2,重定向URL需要配置前端页面的URL,因为获取用户token需要重定向到前端页面,这个地方我们自定义配置即可,授权我们可以访问用户的一些功能权限。

将机器人加入我们的工会

 创建工会(服务器)直接在discord聊天页面创建就好了,这个就不多说了,那么我们如何将我们的机器人拉入到我们的服务器呢?

选择OAuth2,选择bot,勾选机器人的工会权限,然后下面会生成一个链接。

打开链接会出现下图内容,我们将我们的机器人加入到我们自己的服务器即可。 

我们通过上面的步骤成功的创建了我们的应用、工会和机器人,下面我们将介绍如何使用OAuth2相关功能以及操作机器人。

OAuth2功能实现

<!-- Discord4J  依赖 -->
<dependency>
    <groupId>com.discord4j</groupId>
    <artifactId>discord4j-core</artifactId>
    <version>3.2.1</version>
</dependency>

<dependency>
    <groupId>net.dv8tion</groupId>
    <artifactId>JDA</artifactId>
    <version>5.0.0-beta.12</version>
</dependency>

根据配置的OAuth2页面生成相关的链接,用户点击授权以后回调到前端页面,页面会带有code以及state参数,code我们用来换取token,state授权页面我们传什么参数过去会给我们带回来的一个参数,授权类似与下面的地址。

https://discord.com/oauth2/authorize?client_id=1215207180614246411&state=%E9%9A%8F%E6%9C%BA%E5%8F%82%E6%95%B0&response_type=code&redirect_uri=http%3A%2F%2F9cxuu6.natappfree.cc%2Fdiscord%2FgetDiscordByCode&scope=identify+guilds+email+guilds.join+connections+guilds.members.read
 

根据token交互用户数据

拿到token以后我们可以查询用户的信息,用户工会情况等等。

  1. package com.odcchina.server.api;
  2. import com.odcchina.common.config.BaseComponent;
  3. import com.odcchina.server.dto.DiscordDto;
  4. import com.odcchina.server.service.DiscordService;
  5. import com.odcchina.server.service.GuildEvent;
  6. import io.swagger.annotations.Api;
  7. import io.swagger.annotations.ApiOperation;
  8. import lombok.extern.slf4j.Slf4j;
  9. import net.dv8tion.jda.api.JDABuilder;
  10. import net.dv8tion.jda.api.requests.GatewayIntent;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.transaction.annotation.Isolation;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import org.springframework.web.bind.annotation.GetMapping;
  15. import org.springframework.web.bind.annotation.RequestMapping;
  16. import org.springframework.web.bind.annotation.RestController;
  17. import java.util.HashMap;
  18. import java.util.Map;
  19. @Slf4j
  20. @RestController
  21. @RequestMapping("/discord")
  22. @Api(tags = "discord服务")
  23. @Transactional(isolation = Isolation.READ_COMMITTED)
  24. public class DiscordController extends BaseComponent {
  25. @Autowired
  26. private DiscordService discordService;
  27. /**
  28. * 授权回调code
  29. * @param code
  30. * @return
  31. */
  32. @GetMapping("getDiscordByCode")
  33. @ApiOperation("获取discord的Code")
  34. public Map<String, String> getTuiteCode(String code) {
  35. Map<String, String> map = new HashMap<>();
  36. //根据code换取token
  37. DiscordDto discordDto = discordService.getToken(code,null);
  38. map.put("code", code);
  39. map.put("access_token", discordDto.getAccessToken());
  40. map.put("refresh_token", discordDto.getRefreshToken());
  41. return map;
  42. }
  43. @GetMapping("findLaborUnionList")
  44. @ApiOperation("查询我的工会列表")
  45. public Map<String, String> findLaborUnionList(String token) {
  46. Map<String, String> map = new HashMap<>();
  47. try {
  48. discordService.findLaborUnionList(token);
  49. } catch (Exception e) {
  50. throw new RuntimeException(e);
  51. }
  52. return map;
  53. }
  54. @GetMapping("findUserLaborUnion")
  55. @ApiOperation("查询用户是否存在工会里面")
  56. public Map<String, String> findUserLaborUnion(String token,String guildId) {
  57. Map<String, String> map = new HashMap<>();
  58. try {
  59. discordService.findUserLaborUnion(token,guildId);
  60. } catch (Exception e) {
  61. throw new RuntimeException(e);
  62. }
  63. return map;
  64. }
  65. @GetMapping("findUser")
  66. @ApiOperation("查询用户信息")
  67. public Map<String, String> findUser(String token) {
  68. Map<String, String> map = new HashMap<>();
  69. try {
  70. discordService.findUser(token);
  71. } catch (Exception e) {
  72. throw new RuntimeException(e);
  73. }
  74. return map;
  75. }
  76. }
  1. package com.odcchina.server.service;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.github.scribejava.apis.DiscordApi;
  6. import com.odcchina.server.config.DiscordConfig;
  7. import com.odcchina.server.dto.DiscordDto;
  8. import com.odcchina.server.dto.DiscordlaborUnionDto;
  9. import com.odcchina.server.dto.UserDto;
  10. import lombok.extern.slf4j.Slf4j;
  11. import net.dv8tion.jda.api.JDA;
  12. import net.dv8tion.jda.api.JDABuilder;
  13. import org.apache.commons.lang3.StringUtils;
  14. import org.apache.http.HttpEntity;
  15. import org.apache.http.client.ClientProtocolException;
  16. import org.apache.http.client.entity.UrlEncodedFormEntity;
  17. import org.apache.http.client.methods.CloseableHttpResponse;
  18. import org.apache.http.client.methods.HttpGet;
  19. import org.apache.http.client.methods.HttpPost;
  20. import org.apache.http.impl.client.CloseableHttpClient;
  21. import org.apache.http.impl.client.HttpClientBuilder;
  22. import org.apache.http.impl.client.HttpClients;
  23. import org.apache.http.message.BasicNameValuePair;
  24. import org.apache.http.util.EntityUtils;
  25. import org.springframework.stereotype.Service;
  26. import java.io.*;
  27. import java.net.HttpURLConnection;
  28. import java.net.URL;
  29. import java.util.*;
  30. @Slf4j
  31. @Service
  32. public class DiscordService {
  33. /**
  34. * form表单提交
  35. * @param url
  36. * @param map
  37. * @return
  38. */
  39. public DiscordDto doPostForm(String url, Map<String, Object> map) {
  40. String strResult = "";
  41. CloseableHttpClient client = HttpClients.createDefault();
  42. HttpPost httpPost = new HttpPost(url);
  43. httpPost.setHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
  44. List<BasicNameValuePair> paramPairs = new ArrayList<>();
  45. Set<String> keySet = map.keySet();
  46. for (String key : keySet) {
  47. Object val = map.get(key);
  48. paramPairs.add(new BasicNameValuePair(key, val.toString()));
  49. }
  50. UrlEncodedFormEntity entity;
  51. try {
  52. // 4. 将参数设置到entity对象中
  53. entity = new UrlEncodedFormEntity(paramPairs, "UTF-8");
  54. // 5. 将entity对象设置到httppost对象中
  55. httpPost.setEntity(entity);
  56. // 6. 发送请求并回去响应
  57. CloseableHttpResponse resp = client.execute(httpPost);
  58. try {
  59. HttpEntity respEntity = resp.getEntity();
  60. strResult = EntityUtils.toString(respEntity, "UTF-8");
  61. JSONObject json = JSON.parseObject(strResult.toString());
  62. Object access_token = json.get("access_token");
  63. Object refresh_token = json.get("refresh_token");
  64. Object token_type = json.get("token_type");
  65. Object expires_in = json.get("expires_in");
  66. if(access_token == null || refresh_token == null){
  67. return null;
  68. }
  69. DiscordDto discordDto = new DiscordDto();
  70. discordDto.setAccessToken(access_token.toString());
  71. discordDto.setRefreshToken(refresh_token.toString());
  72. discordDto.setTokenType(token_type.toString());
  73. discordDto.setExpiresIn(Integer.parseInt(expires_in.toString()));
  74. return discordDto;
  75. } finally {
  76. resp.close();
  77. }
  78. } catch (ClientProtocolException e) {
  79. e.printStackTrace();
  80. } catch (UnsupportedEncodingException e) {
  81. e.printStackTrace();
  82. } catch (IOException e) {
  83. e.printStackTrace();
  84. } finally {
  85. // 10. 关闭连接,释放资源
  86. try {
  87. client.close();
  88. } catch (Exception e) {
  89. e.printStackTrace();
  90. }
  91. }
  92. return null;
  93. }
  94. /**
  95. * 根据code换取token
  96. *
  97. * @param code
  98. * @return
  99. */
  100. public DiscordDto getToken(String code,String refreshToken) {
  101. try {
  102. Map<String, Object> formData = new HashMap<>();
  103. if(!StringUtils.isBlank(code)){
  104. formData.put("grant_type", "authorization_code");
  105. formData.put("code", code);
  106. }
  107. if(!StringUtils.isBlank(refreshToken)){
  108. formData.put("refresh_token", refreshToken);
  109. formData.put("grant_type", "refresh_token");
  110. }
  111. //TODO 重定向配置的URL,一定要和重定向里面的地址保持一致
  112. formData.put("redirect_uri", 重定向配置的地址URL");
  113. formData.put("client_id", DiscordConfig.CLIENT_ID);
  114. formData.put("client_secret", DiscordConfig.CLIENT_SECRET);
  115. // 创建URL对象
  116. DiscordDto discordDto = this.doPostForm("https://discord.com/api/oauth2/token",formData);
  117. return discordDto;
  118. } catch (Exception e) {
  119. throw new RuntimeException(e);
  120. }
  121. }
  122. /**
  123. * 获取用户的所有服务器
  124. * @param token
  125. * @return
  126. */
  127. public List<DiscordlaborUnionDto> findLaborUnionList(String token) {
  128. try {
  129. // 设置请求URL和Bearer Token
  130. CloseableHttpClient httpClient = HttpClientBuilder.create().build();
  131. HttpGet httpPost = new HttpGet("https://discord.com/api/v9/users/@me/guilds");
  132. httpPost.setHeader("Content-Type", "application/json;charset=utf8");
  133. httpPost.setHeader("Authorization", "Bearer " + token);
  134. CloseableHttpResponse response = null;
  135. // 由客户端执行(发送)Post请求
  136. response = httpClient.execute(httpPost);
  137. // 从响应模型中获取响应实体
  138. HttpEntity responseEntity = response.getEntity();
  139. if (response.getStatusLine().getStatusCode() == 200 && responseEntity != null) {
  140. String responseJson = EntityUtils.toString(responseEntity);
  141. JSONArray jsonArray = JSON.parseArray(responseJson);
  142. List<DiscordlaborUnionDto> jsonArrayToStringList = JSONObject.parseArray(jsonArray.toJSONString(),DiscordlaborUnionDto.class);
  143. return jsonArrayToStringList;
  144. }
  145. } catch (Exception e) {
  146. e.printStackTrace();
  147. }
  148. return null;
  149. }
  150. public Boolean findUserLaborUnion(String token, String guildId) {
  151. try {
  152. // 设置请求URL和Bearer Token
  153. CloseableHttpClient httpClient = HttpClientBuilder.create().build();
  154. HttpGet httpPost = new HttpGet("https://discord.com/api/v9/users/@me/guilds/" + guildId+"/member");
  155. httpPost.setHeader("Content-Type", "application/json;charset=utf8");
  156. httpPost.setHeader("Authorization", "Bearer " + token);
  157. CloseableHttpResponse response = null;
  158. // 由客户端执行(发送)Post请求
  159. response = httpClient.execute(httpPost);
  160. // 从响应模型中获取响应实体
  161. HttpEntity responseEntity = response.getEntity();
  162. if (response.getStatusLine().getStatusCode() == 200 && responseEntity != null) {
  163. String responseJson = EntityUtils.toString(responseEntity);
  164. JSONObject jsonArray = JSON.parseObject(responseJson);
  165. if(jsonArray.get("user") != null){
  166. return true;
  167. }
  168. }
  169. } catch (Exception e) {
  170. e.printStackTrace();
  171. }
  172. return false;
  173. }
  174. /**
  175. * 查询用户信息
  176. * @param token
  177. * @return
  178. */
  179. public UserDto findUser(String token) {
  180. try {
  181. // 设置请求URL和Bearer Token
  182. CloseableHttpClient httpClient = HttpClientBuilder.create().build();
  183. HttpGet httpPost = new HttpGet("https://discord.com/api/v9/users/@me");
  184. httpPost.setHeader("Content-Type", "application/json;charset=utf8");
  185. httpPost.setHeader("Authorization", "Bearer " + token);
  186. CloseableHttpResponse response = null;
  187. // 由客户端执行(发送)Post请求
  188. response = httpClient.execute(httpPost);
  189. // 从响应模型中获取响应实体
  190. HttpEntity responseEntity = response.getEntity();
  191. if (response.getStatusLine().getStatusCode() == 200 && responseEntity != null) {
  192. String responseJson = EntityUtils.toString(responseEntity);
  193. JSONObject jsonArray = JSON.parseObject(responseJson);
  194. UserDto userDto = JSON.toJavaObject(jsonArray, UserDto.class);
  195. return userDto;
  196. }
  197. } catch (Exception e) {
  198. e.printStackTrace();
  199. }
  200. return null;
  201. }
  202. }
  1. package com.odcchina.server.dto;
  2. import lombok.Data;
  3. import lombok.experimental.Accessors;
  4. @Data
  5. @Accessors(chain = true)
  6. public class DiscordDto {
  7. /**
  8. * 访问令牌
  9. */
  10. private String accessToken;
  11. /**
  12. * 刷新令牌
  13. */
  14. private String refreshToken;
  15. /**
  16. * 认证方式 Bearer
  17. */
  18. private String tokenType;
  19. /**
  20. * 过期时间 (秒)
  21. */
  22. private Integer expiresIn;
  23. }
  1. package com.odcchina.server.dto;
  2. import lombok.Data;
  3. import lombok.experimental.Accessors;
  4. /**
  5. * 获取工会列表
  6. */
  7. @Data
  8. @Accessors(chain = true)
  9. public class DiscordlaborUnionDto {
  10. /**
  11. * 工会ID
  12. */
  13. public String id;
  14. /**
  15. * 工会名称
  16. */
  17. public String name;
  18. }
  1. package com.odcchina.server.dto;
  2. import lombok.Data;
  3. import lombok.experimental.Accessors;
  4. @Data
  5. @Accessors(chain = true)
  6. public class DiscordUserDto {
  7. /**
  8. * 服务器ID
  9. */
  10. private String guildId;
  11. /**
  12. * 服务器名称
  13. */
  14. private String guildName;
  15. /**
  16. * 用户ID
  17. */
  18. private String userId;
  19. /**
  20. * 用户名称
  21. */
  22. private String userName;
  23. }
  1. package com.odcchina.server.config;
  2. /**
  3. * Discord相关配置
  4. */
  5. public class DiscordConfig {
  6. /**
  7. * 客户id和客户私钥
  8. */
  9. public static final String CLIENT_ID = "111111";
  10. public static final String CLIENT_SECRET = "nL8gLAmZEFZYtQQ2mqE3tEYWC111111";
  11. public static final String PUBLIC_CLIENT_SECRET = "feca6db06a1af3c5ebff8fb3710213b86651f6401623d103a5111111";
  12. public static final String TOKEN_ENDPOINT = "https://discord.com/api/oauth2/token";
  13. }

使用机器人监听工会会员变动情况

工会会员新加入,退出等情况监听,前期我们需要开启下面这些功能,不然机器人无法监听:

  1. @GetMapping("botMonitor")
  2. @ApiOperation("开启机器人监听服务")
  3. public Map<String, String> botMonitor() {
  4. //启动机器人,监听成员事件
  5. try {
  6. //这里是机器人的token的,注意下
  7. String botToken = "1111.Gh00hD.1111";
  8. JDABuilder builder = JDABuilder.createDefault(botToken);
  9. //添加事件监听器
  10. builder.addEventListeners(new GuildEvent());
  11. //builder.useSharding(int shardId, int shardTotal)
  12. //工会成员 GUILD_MEMBERS TODO 这个地方在机器人里面有个选项需要开启,不然无法调用
  13. builder.enableIntents(GatewayIntent.GUILD_MEMBERS);
  14. builder.build();
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }

当我们启动以后我们的机器人就会在线,由于我没有启动监听,所以我们的机器人在离线中的:

  1. package com.odcchina.server.service;
  2. import com.odcchina.server.dto.DiscordUserDto;
  3. import net.dv8tion.jda.api.entities.Guild;
  4. import net.dv8tion.jda.api.entities.User;
  5. import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
  6. import net.dv8tion.jda.api.events.guild.invite.GuildInviteCreateEvent;
  7. import net.dv8tion.jda.api.events.guild.invite.GuildInviteDeleteEvent;
  8. import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
  9. import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
  10. import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleAddEvent;
  11. import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleRemoveEvent;
  12. import net.dv8tion.jda.api.hooks.ListenerAdapter;
  13. import javax.annotation.Nonnull;
  14. public class GuildEvent extends ListenerAdapter {
  15. @Override
  16. public void onGuildInviteCreate(@Nonnull GuildInviteCreateEvent event) {
  17. //一个邀请被创建了
  18. }
  19. @Override
  20. public void onGuildInviteDelete(@Nonnull GuildInviteDeleteEvent event) {
  21. //一个邀请被删除了
  22. }
  23. @Override
  24. public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
  25. //有新成员进入公会
  26. Guild guild = event.getGuild();
  27. User user = event.getUser();
  28. DiscordUserDto discordUserDto = new DiscordUserDto();
  29. discordUserDto.setGuildId(guild.getId());
  30. discordUserDto.setGuildName(guild.getName());
  31. discordUserDto.setUserId(user.getId());
  32. discordUserDto.setUserName(user.getName());
  33. }
  34. @Override
  35. public void onGuildLeave(@Nonnull GuildLeaveEvent event) {
  36. //有老成员离开公会
  37. }
  38. @Override
  39. public void onGuildMemberRemove(@Nonnull GuildMemberRemoveEvent event) {
  40. //有成员被移除公会
  41. }
  42. @Override
  43. public void onGuildMemberRoleAdd(@Nonnull GuildMemberRoleAddEvent event) {
  44. //公会成员添加角色
  45. }
  46. @Override
  47. public void onGuildMemberRoleRemove(@Nonnull GuildMemberRoleRemoveEvent event) {
  48. //公会成员移除角色
  49. }
  50. }

机器人其他功能调用,比如查询邀请信息,查询邀请我的频道会员

https://discord.com/api/v9/channels/工会ID/invites

关于其他机器人功能实现可以参考下面的文档,大佬写的很详细,点这里 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号