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


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

    创建应用

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

    创建机器人

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


    配置OAuth2

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

    将机器人加入我们的工会

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

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

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

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

    OAuth2功能实现

    
    
        com.discord4j
        discord4j-core
        3.2.1
    
    
    
        net.dv8tion
        JDA
        5.0.0-beta.12
    

    根据配置的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 getTuiteCode(String code) {
    35. Map 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 findLaborUnionList(String token) {
    46. Map 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 findUserLaborUnion(String token,String guildId) {
    57. Map 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 findUser(String token) {
    68. Map 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 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 paramPairs = new ArrayList<>();
    45. Set 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 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 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 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 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

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

  • 相关阅读:
    30分钟使用百度EasyDL实现烟雾检测
    Zookeeper客户端命令、JAVA API、监听原理、写数据原理以及案例
    鸿蒙Harmony应用开发—ArkTS声明式开发(触摸事件)
    数据结构与算法 | 哈希表(Hash Table)
    软考-系统架构-2023-反思
    想在golang里用好泛型还挺难的
    【华为OD机试真题 JS】数组拼接
    java计算机毕业设计学科竞赛管理系统源码+数据库+系统+lw文档+部署
    台式电脑的IP地址在哪里?解密台式电脑网络连接的秘密!
    nodejs--开发自己的项目——5.2——个人中心模块——重置密码——设置的url:/my/updatepwd——post请求
  • 原文地址:https://blog.csdn.net/qq_38935605/article/details/136619200