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以后我们可以查询用户的信息,用户工会情况等等。

package com.odcchina.server.api;

import com.odcchina.common.config.BaseComponent;
import com.odcchina.server.dto.DiscordDto;
import com.odcchina.server.service.DiscordService;
import com.odcchina.server.service.GuildEvent;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.requests.GatewayIntent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/discord")
@Api(tags = "discord服务")
@Transactional(isolation = Isolation.READ_COMMITTED)
public class DiscordController extends BaseComponent {

    @Autowired
    private DiscordService discordService;

    /**
     * 授权回调code
     * @param code
     * @return
     */
    @GetMapping("getDiscordByCode")
    @ApiOperation("获取discord的Code")
    public Map<String, String> getTuiteCode(String code) {
        Map<String, String> map = new HashMap<>();
        //根据code换取token
        DiscordDto discordDto = discordService.getToken(code,null);
        map.put("code", code);
        map.put("access_token", discordDto.getAccessToken());
        map.put("refresh_token", discordDto.getRefreshToken());
        return map;
    }

    @GetMapping("findLaborUnionList")
    @ApiOperation("查询我的工会列表")
    public Map<String, String> findLaborUnionList(String token) {
        Map<String, String> map = new HashMap<>();
        try {
            discordService.findLaborUnionList(token);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return map;
    }

    @GetMapping("findUserLaborUnion")
    @ApiOperation("查询用户是否存在工会里面")
    public Map<String, String> findUserLaborUnion(String token,String guildId) {
        Map<String, String> map = new HashMap<>();
        try {
            discordService.findUserLaborUnion(token,guildId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return map;
    }

    @GetMapping("findUser")
    @ApiOperation("查询用户信息")
    public Map<String, String> findUser(String token) {
        Map<String, String> map = new HashMap<>();
        try {
            discordService.findUser(token);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return map;
    }
    
}

package com.odcchina.server.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.scribejava.apis.DiscordApi;
import com.odcchina.server.config.DiscordConfig;
import com.odcchina.server.dto.DiscordDto;
import com.odcchina.server.dto.DiscordlaborUnionDto;
import com.odcchina.server.dto.UserDto;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

@Slf4j
@Service
public class DiscordService {

    /**
     * form表单提交
     * @param url
     * @param map
     * @return
     */
    public DiscordDto doPostForm(String url, Map<String, Object> map) {
        String strResult = "";
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
        List<BasicNameValuePair> paramPairs = new ArrayList<>();
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            Object val = map.get(key);
            paramPairs.add(new BasicNameValuePair(key, val.toString()));
        }
        UrlEncodedFormEntity entity;
        try {
            // 4. 将参数设置到entity对象中
            entity = new UrlEncodedFormEntity(paramPairs, "UTF-8");
            // 5. 将entity对象设置到httppost对象中
            httpPost.setEntity(entity);
            // 6. 发送请求并回去响应
            CloseableHttpResponse resp = client.execute(httpPost);
            try {
                HttpEntity respEntity = resp.getEntity();
                strResult = EntityUtils.toString(respEntity, "UTF-8");
                JSONObject json = JSON.parseObject(strResult.toString());
                Object access_token = json.get("access_token");
                Object refresh_token = json.get("refresh_token");
                Object token_type = json.get("token_type");
                Object expires_in = json.get("expires_in");
                if(access_token == null || refresh_token == null){
                    return null;
                }
                DiscordDto discordDto = new DiscordDto();
                discordDto.setAccessToken(access_token.toString());
                discordDto.setRefreshToken(refresh_token.toString());
                discordDto.setTokenType(token_type.toString());
                discordDto.setExpiresIn(Integer.parseInt(expires_in.toString()));
                return discordDto;
            } finally {
                resp.close();
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 10. 关闭连接,释放资源
            try {
                client.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 根据code换取token
     *
     * @param code
     * @return
     */
    public DiscordDto getToken(String code,String refreshToken) {
        try {
            Map<String, Object> formData = new HashMap<>();
            if(!StringUtils.isBlank(code)){
                formData.put("grant_type", "authorization_code");
                formData.put("code", code);
            }
            if(!StringUtils.isBlank(refreshToken)){
                formData.put("refresh_token", refreshToken);
                formData.put("grant_type", "refresh_token");
            }
            //TODO 重定向配置的URL,一定要和重定向里面的地址保持一致
            formData.put("redirect_uri", 重定向配置的地址URL");
            formData.put("client_id", DiscordConfig.CLIENT_ID);
            formData.put("client_secret", DiscordConfig.CLIENT_SECRET);
            // 创建URL对象
            DiscordDto discordDto = this.doPostForm("https://discord.com/api/oauth2/token",formData);
            return discordDto;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取用户的所有服务器
     * @param token
     * @return
     */
    public List<DiscordlaborUnionDto> findLaborUnionList(String token) {
        try {
            // 设置请求URL和Bearer Token
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpGet httpPost = new HttpGet("https://discord.com/api/v9/users/@me/guilds");
            httpPost.setHeader("Content-Type", "application/json;charset=utf8");
            httpPost.setHeader("Authorization", "Bearer " + token);
            CloseableHttpResponse response = null;
            // 由客户端执行(发送)Post请求
            response = httpClient.execute(httpPost);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200 && responseEntity != null) {
                String responseJson = EntityUtils.toString(responseEntity);
                JSONArray jsonArray = JSON.parseArray(responseJson);
                List<DiscordlaborUnionDto> jsonArrayToStringList = JSONObject.parseArray(jsonArray.toJSONString(),DiscordlaborUnionDto.class);
                return jsonArrayToStringList;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Boolean findUserLaborUnion(String token, String guildId) {
        try {
            // 设置请求URL和Bearer Token
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpGet httpPost = new HttpGet("https://discord.com/api/v9/users/@me/guilds/" + guildId+"/member");
            httpPost.setHeader("Content-Type", "application/json;charset=utf8");
            httpPost.setHeader("Authorization", "Bearer " + token);
            CloseableHttpResponse response = null;
            // 由客户端执行(发送)Post请求
            response = httpClient.execute(httpPost);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200 && responseEntity != null) {
                String responseJson = EntityUtils.toString(responseEntity);
                JSONObject jsonArray = JSON.parseObject(responseJson);
                if(jsonArray.get("user") != null){
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 查询用户信息
     * @param token
     * @return
     */
    public UserDto findUser(String token) {
        try {
            // 设置请求URL和Bearer Token
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpGet httpPost = new HttpGet("https://discord.com/api/v9/users/@me");
            httpPost.setHeader("Content-Type", "application/json;charset=utf8");
            httpPost.setHeader("Authorization", "Bearer " + token);
            CloseableHttpResponse response = null;
            // 由客户端执行(发送)Post请求
            response = httpClient.execute(httpPost);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200 && responseEntity != null) {
                String responseJson = EntityUtils.toString(responseEntity);
                JSONObject jsonArray = JSON.parseObject(responseJson);
                UserDto userDto = JSON.toJavaObject(jsonArray, UserDto.class);
                return userDto;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
package com.odcchina.server.dto;

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class DiscordDto {

    /**
     * 访问令牌
     */
    private String accessToken;

    /**
     * 刷新令牌
     */
    private String refreshToken;

    /**
     * 认证方式 Bearer
     */
    private String tokenType;

    /**
     * 过期时间 (秒)
     */
    private Integer expiresIn;

}
package com.odcchina.server.dto;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 获取工会列表
 */
@Data
@Accessors(chain = true)
public class DiscordlaborUnionDto {


    /**
     * 工会ID
     */
    public String id;


    /**
     * 工会名称
     */
    public String name;

}
package com.odcchina.server.dto;

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class DiscordUserDto {

    /**
     * 服务器ID
     */
    private String guildId;

    /**
     * 服务器名称
     */
    private String guildName;

    /**
     * 用户ID
     */
    private String userId;

    /**
     * 用户名称
     */
    private String userName;

}
package com.odcchina.server.config;

/**
 * Discord相关配置
 */
public class DiscordConfig {

    /**
     * 客户id和客户私钥
     */
    public static final String CLIENT_ID = "111111";
    public static final String CLIENT_SECRET = "nL8gLAmZEFZYtQQ2mqE3tEYWC111111";
    public static final String PUBLIC_CLIENT_SECRET = "feca6db06a1af3c5ebff8fb3710213b86651f6401623d103a5111111";

    public static final String TOKEN_ENDPOINT = "https://discord.com/api/oauth2/token";
}

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

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

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

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

package com.odcchina.server.service;

import com.odcchina.server.dto.DiscordUserDto;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
import net.dv8tion.jda.api.events.guild.invite.GuildInviteCreateEvent;
import net.dv8tion.jda.api.events.guild.invite.GuildInviteDeleteEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleAddEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import javax.annotation.Nonnull;

public class GuildEvent extends ListenerAdapter {

    @Override
    public void onGuildInviteCreate(@Nonnull GuildInviteCreateEvent event) {
        //一个邀请被创建了
    }

    @Override
    public void onGuildInviteDelete(@Nonnull GuildInviteDeleteEvent event) {
        //一个邀请被删除了
    }

    @Override
    public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
        //有新成员进入公会
        Guild guild = event.getGuild();
        User user = event.getUser();
        DiscordUserDto discordUserDto = new DiscordUserDto();
        discordUserDto.setGuildId(guild.getId());
        discordUserDto.setGuildName(guild.getName());
        discordUserDto.setUserId(user.getId());
        discordUserDto.setUserName(user.getName());
    }

    @Override
    public void onGuildLeave(@Nonnull GuildLeaveEvent event) {
        //有老成员离开公会
    }

    @Override
    public void onGuildMemberRemove(@Nonnull GuildMemberRemoveEvent event) {
        //有成员被移除公会
    }

    @Override
    public void onGuildMemberRoleAdd(@Nonnull GuildMemberRoleAddEvent event) {
        //公会成员添加角色
    }

    @Override
    public void onGuildMemberRoleRemove(@Nonnull GuildMemberRoleRemoveEvent event) {
        //公会成员移除角色
    }
}

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/449425.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用kettle批量加载数据到kadb

测试环境 达梦数据库版本&#xff1a;DM Database Server 64 V8 03134284132-20240115-215128-20081&#xff08;官网测试版&#xff09;KADB版本&#xff1a;KADB V003R002C001B0181Kettle版本&#xff1a;pdi-ce-9.4.0.0-343&#xff08;官网下载&#xff09;Python版本&…

解释“RNN encode-decode”

“RNN encode-decode” 涉及使用循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;来执行编码和解码操作。这种结构常用于处理序列数据&#xff0c;例如自然语言处理、语音识别和时间序列预测等任务。 以下是 “RNN encode-decode” 的一般概念&a…

Flink实操:Flink SQL实现SFTP文件读写操作

一、背景 公司需要将Doris数据库中的部分表数据同步至SFTP服务器&#xff0c;以供其他合作企业安全读取和使用。目前&#xff0c;平台数据同步功能统一使用Flink引擎进行实时同步、离线同步的工作。因此&#xff0c;希望能够充分利用现有的Flink引擎&#xff0c;并将其复用于这…

四 超级数据查看器 讲解稿 列表功能1

四 超级数据查看器 讲解稿 列表功能1 点击此处 以新页面 打开B站 播放教学视频 APP下载地址 百度手机助手 下载地址4 讲解稿全文&#xff1a; 大家好&#xff0c;今天我们讲解一下&#xff0c;超级数据查看器列表界面&#xff0c;分为1-2两集。 首先&#xff0c…

ChatGPT+MATLAB应用

MatGPT是一个由chatGPT类支持的MATLAB应用程序&#xff0c;由官方Toshiaki Takeuchi开发&#xff0c;允许您轻松访问OpenAI提供的chatGPT API。作为官方发布的内容&#xff0c;可靠性较高&#xff0c;而且也是完全免费开源的&#xff0c;全程自己配置&#xff0c;无需注册码或用…

MySQL的加锁规则

学习了MySQL的锁后&#xff0c;知道其有这么多锁&#xff0c;那应该会有些疑惑&#xff0c;这么多锁&#xff0c;究竟我在写sql语句时候用到哪个锁的&#xff0c;什么情况是用什么锁的&#xff1f;在哪里查看该sql语句是用了哪些锁的呢&#xff1f;加锁的规则是什么呢&#xff…

【C++初阶】第六站 : 模板初阶

前言&#xff1a; 本章知识点&#xff1a;泛型编程、函数模板、类模板 专栏&#xff1a; C初阶 目录 泛型编程 函数模板 1.函数模板概念 2.函数模板格式 3.函数模板的原理 4.函数模板的实例化 5.模板参数的匹配原则 类模板 类模板的定义格式 类模板的实例化 泛型编程 如何实现一…

Redis 的基本全局命令

前言 Redis 常用的有 5 种数据结构&#xff0c;字符串&#xff0c;列表&#xff0c;哈希表&#xff0c;集合&#xff0c;有序集合&#xff0c;每一种数据结构都有自己独特的命令&#xff0c;但也有些通用的全局命令&#xff0c;本文所提到的是最基本的命令&#xff0c;Redis 的…

linux查看文件内容cat,less,vi,vim

学习记录 目录 catlessvi vim cat 输出 FILE 文件的全部内容 $ cat [OPTION] FILE示例 输出 file.txt 的全部内容 $ cat file.txt查看 file1.txt 与 file2.txt 连接后的内容 $ cat file1.txt file2.txt为什么名字叫 cat&#xff1f; 当然和猫咪没有关系。 cat 这里是 co…

使用 IDEA 将本地jar上传到本地maven仓库

IDEA中的操作步骤 创建一个 Maven 运行配置 在开发工具的导航栏中&#xff0c;点击选择配置&#xff1a; 在配置界面点击左上角的加号&#xff0c;随后选择增加一个maven运行配置&#xff1a; 编辑 Maven 配置 上图中的含义&#xff1a; Name 对应的是本配置的名字、用处或功…

PyTorch搭建AlexNet训练集

本次项目是使用AlexNet实现5种花类的识别。 训练集搭建与LeNet大致代码差不多&#xff0c;但是也有许多新的内容和知识点。 1.导包&#xff0c;不必多说。 import torch import torch.nn as nn from torchvision import transforms, datasets, utils import matplotlib as p…

NFTScan | 03.04~03.10 NFT 市场热点汇总

欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期&#xff1a;2024.03.04~ 2024.03.10 NFT Hot News 01/ 数据&#xff1a;比特币链上 NFT 过去 24 小时销售额超 3100 万美元 3 月 4 日&#xff0c;据数据显示&#xff0c;比特币链上 NFT 过去 24 小…

设计模式十:原型模式

文章目录 1、原型模式1.1 类创建过程1.2 浅拷贝1.3 深拷贝 2、示例2.1 简单形式2.2 复杂形式 3、spring中的原型模式3.1 ArrayList的原型模式3.2 spring中的原型模式 1、原型模式 原型模式就是从一个对象再创建另外一个可定制的对象&#xff0c; 而且不需要知道任何创建的细节。…

Vscode+QT+Python

参考链接&#xff1a;VSCodePyQt之Python界面编写_vscode编写图形化界面-CSDN博客 1.安装库 pip install PyQt5 pip install PyQt5-tools pip install qt5_applications 2.在VSCode里下载并安装PYQT Integration 3.配置pyqt integration 4.打开qt designer 在工程文件的空白…

python自动化之pytest框架以及数据驱动(第五天)

1.pytest框架需要遵循的规则 &#xff08;1&#xff09;.py 测试文件必须以test 开头(或者以 test结尾) &#xff08;2&#xff09;测试类必须以Test开头&#xff0c;并且不能有 init 方法 &#xff08;3&#xff09;测试方法必须以test 开头 &#xff08;4&#xff09;断言…

分享个好用的GPT网站

目录 一、背景 二、功能描述 1、写代码 2、联网查询 3、AI绘图 一、背景 我现在的开发工作都依靠ChatGPT&#xff0c;效率提升了好几倍。这样一来&#xff0c;我有更多时间来摸鱼&#xff0c;真是嘎嘎香~ ⭐⭐⭐点击直达 ⭐⭐⭐ 二、功能描述 1、写代码 import java.ut…

机器学习之分类回归模型(决策数、随机森林)

回归分析 回归分析属于监督学习方法的一种&#xff0c;主要用于预测连续型目标变量&#xff0c;可以预测、计算趋势以及确定变量之间的关系等。 Regession Evaluation Metrics 以下是一些最流行的回归评估指标: 平均绝对误差(MAE):目标变量的预测值与实际值之间的平均绝对差…

基于PHP+Amaze+JQuery的学习论坛的设计与实现1.99

摘 要 互联网教育服务是在互联网技术、通信技术、计算机技术不断发展融合的基础之上&#xff0c;人们在对以信息为基础的各种各样应用需求快速增长的激励之下&#xff0c;在现在社会信息化的水平日益提高前提之下&#xff0c;迅速发展起来的一种全新大众服务方式。 笔者拟设计…

前端食堂技术周刊第 115 期:Rolldown 正式开源、马斯克宣布 xAI 本周将开源 Grok、如何使用 Copilot 完成 50% 的日常工作?

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;手打柠檬茶 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看…

Docker的安装及镜像加速的配置

文章目录 一.切换到root二.卸载旧版docker三.配置docker的yum库四.安装Docker五.Docker的启动和验证六.配置Docker阿里云镜像加速(全程免费) 该文章文章演示在Linux系统中安装docker&#xff0c;Windows安装docker请参考以下文章 Windows系统中安装docker及镜像加速的配置 一…