JAVA对接钉钉

正文

公司要求对接钉钉,之前没对接过,相当于从0开始,记录一下对接的过程

1、访问钉钉官网

申请成为开发者

因为公司没对接过,所以我自己注册了一个公司(公司名随便填,其他的按要求填就好)
在这里插入图片描述
注意:如果你本身就是最高级的管理员,那么你不需要申请成为开发者,因为你就是BOSS
在这里插入图片描述
如果你不是最高级,那么,你就要申请成为开发者,权限叫你的同事配上去

2、创建一个应用

在这里插入图片描述

3、点击应用详情

在这里插入图片描述

4、选择事件与回调

在这里插入图片描述

5、做内网穿透

工具自己上网找一个,要达到的效果是,可以通过外网访问项目。例如:访问接口:localhost:80/接口名,外网的访问方式是:http:/外网映射:外网端口/接口名

具体的映射参考工具的教程

6、测试代码

DingCallbackCrypto(这个类是从阿里的git拿下来的,里面还有其他语言的,这里是java代码)

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.security.Security;
import java.lang.reflect.Field;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;import com.alibaba.fastjson.JSON;import org.apache.commons.codec.binary.Base64;
/*** @Description DingDing解密* @Author JunHao Huang* @Date 2023/6/1 10:25*/
public class DingCallbackCrypto {private static final Charset CHARSET = Charset.forName("utf-8");private static final Base64 base64 = new Base64();private byte[] aesKey;private String token;private String corpId;/*** ask getPaddingBytes key固定长度**/private static final Integer AES_ENCODE_KEY_LENGTH = 43;/*** 加密随机字符串字节长度**/private static final Integer RANDOM_LENGTH = 16;/*** 构造函数** @param token          钉钉开放平台上,开发者设置的token* @param encodingAesKey 钉钉开放台上,开发者设置的EncodingAESKey* @param corpId         企业自建应用-事件订阅, 使用appKey*                       企业自建应用-注册回调地址, 使用corpId*                       第三方企业应用, 使用suiteKey** @throws DingTalkEncryptException 执行失败,请查看该异常的错误码和具体的错误信息*/public DingCallbackCrypto(String token, String encodingAesKey, String corpId) throws DingTalkEncryptException {if (null == encodingAesKey || encodingAesKey.length() != AES_ENCODE_KEY_LENGTH) {throw new DingTalkEncryptException(DingTalkEncryptException.AES_KEY_ILLEGAL);}this.token = token;this.corpId = corpId;aesKey = Base64.decodeBase64(encodingAesKey + "=");}public Map<String, String> getEncryptedMap(String plaintext) throws DingTalkEncryptException {return getEncryptedMap(plaintext, System.currentTimeMillis(), Utils.getRandomStr(16));}/*** 将和钉钉开放平台同步的消息体加密,返回加密Map** @param plaintext 传递的消息体明文* @param timeStamp 时间戳* @param nonce     随机字符串* @return* @throws DingTalkEncryptException*/public Map<String, String> getEncryptedMap(String plaintext, Long timeStamp, String nonce)throws DingTalkEncryptException {if (null == plaintext) {throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);}if (null == timeStamp) {throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);}if (null == nonce) {throw new DingTalkEncryptException(DingTalkEncryptException.ENCRYPTION_NONCE_ILLEGAL);}// 加密String encrypt = encrypt(Utils.getRandomStr(RANDOM_LENGTH), plaintext);String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);Map<String, String> resultMap = new HashMap<String, String>();resultMap.put("msg_signature", signature);resultMap.put("encrypt", encrypt);resultMap.put("timeStamp", String.valueOf(timeStamp));resultMap.put("nonce", nonce);return resultMap;}/*** 密文解密** @param msgSignature 签名串* @param timeStamp    时间戳* @param nonce        随机串* @param encryptMsg   密文* @return 解密后的原文* @throws DingTalkEncryptException*/public String getDecryptMsg(String msgSignature, String timeStamp, String nonce, String encryptMsg)throws DingTalkEncryptException {//校验签名String signature = getSignature(token, timeStamp, nonce, encryptMsg);if (!signature.equals(msgSignature)) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);}// 解密String result = decrypt(encryptMsg);return result;}/** 对明文加密.* @param text 需要加密的明文* @return 加密后base64编码的字符串*/private String encrypt(String random, String plaintext) throws DingTalkEncryptException {try {byte[] randomBytes = random.getBytes(CHARSET);byte[] plainTextBytes = plaintext.getBytes(CHARSET);byte[] lengthByte = Utils.int2Bytes(plainTextBytes.length);byte[] corpidBytes = corpId.getBytes(CHARSET);ByteArrayOutputStream byteStream = new ByteArrayOutputStream();byteStream.write(randomBytes);byteStream.write(lengthByte);byteStream.write(plainTextBytes);byteStream.write(corpidBytes);byte[] padBytes = PKCS7Padding.getPaddingBytes(byteStream.size());byteStream.write(padBytes);byte[] unencrypted = byteStream.toByteArray();byteStream.close();Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);byte[] encrypted = cipher.doFinal(unencrypted);String result = base64.encodeToString(encrypted);return result;} catch (Exception e) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);}}/** 对密文进行解密.* @param text 需要解密的密文* @return 解密得到的明文*/private String decrypt(String text) throws DingTalkEncryptException {byte[] originalArr;try {// 设置解密模式为AES的CBC模式Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);// 使用BASE64对密文进行解码byte[] encrypted = Base64.decodeBase64(text);// 解密originalArr = cipher.doFinal(encrypted);} catch (Exception e) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);}String plainText;String fromCorpid;try {// 去除补位字符byte[] bytes = PKCS7Padding.removePaddingBytes(originalArr);// 分离16位随机字符串,网络字节序和corpIdbyte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int plainTextLegth = Utils.bytes2int(networkOrder);plainText = new String(Arrays.copyOfRange(bytes, 20, 20 + plainTextLegth), CHARSET);fromCorpid = new String(Arrays.copyOfRange(bytes, 20 + plainTextLegth, bytes.length), CHARSET);} catch (Exception e) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_LENGTH_ERROR);}// corpid不相同的情况if (!fromCorpid.equals(corpId)) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_DECRYPT_TEXT_CORPID_ERROR);}return plainText;}/*** 数字签名** @param token     isv token* @param timestamp 时间戳* @param nonce     随机串* @param encrypt   加密文本* @return* @throws DingTalkEncryptException*/public String getSignature(String token, String timestamp, String nonce, String encrypt)throws DingTalkEncryptException {try {String[] array = new String[] {token, timestamp, nonce, encrypt};Arrays.sort(array);System.out.println(JSON.toJSONString(array));StringBuffer sb = new StringBuffer();for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();System.out.println(str);MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (Exception e) {throw new DingTalkEncryptException(DingTalkEncryptException.COMPUTE_SIGNATURE_ERROR);}}public static class Utils {public Utils() {}public static String getRandomStr(int count) {String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < count; ++i) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}public static byte[] int2Bytes(int count) {byte[] byteArr = new byte[] {(byte)(count >> 24 & 255), (byte)(count >> 16 & 255), (byte)(count >> 8 & 255),(byte)(count & 255)};return byteArr;}public static int bytes2int(byte[] byteArr) {int count = 0;for (int i = 0; i < 4; ++i) {count <<= 8;count |= byteArr[i] & 255;}return count;}}public static class PKCS7Padding {private static final Charset CHARSET = Charset.forName("utf-8");private static final int BLOCK_SIZE = 32;public PKCS7Padding() {}public static byte[] getPaddingBytes(int count) {int amountToPad = 32 - count % 32;if (amountToPad == 0) {amountToPad = 32;}char padChr = chr(amountToPad);String tmp = new String();for (int index = 0; index < amountToPad; ++index) {tmp = tmp + padChr;}return tmp.getBytes(CHARSET);}public static byte[] removePaddingBytes(byte[] decrypted) {int pad = decrypted[decrypted.length - 1];if (pad < 1 || pad > 32) {pad = 0;}return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);}private static char chr(int a) {byte target = (byte)(a & 255);return (char)target;}}public static class DingTalkEncryptException extends Exception {public static final int SUCCESS = 0;public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 900001;public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 900002;public static final int ENCRYPTION_NONCE_ILLEGAL = 900003;public static final int AES_KEY_ILLEGAL = 900004;public static final int SIGNATURE_NOT_MATCH = 900005;public static final int COMPUTE_SIGNATURE_ERROR = 900006;public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 900007;public static final int COMPUTE_DECRYPT_TEXT_ERROR = 900008;public static final int COMPUTE_DECRYPT_TEXT_LENGTH_ERROR = 900009;public static final int COMPUTE_DECRYPT_TEXT_CORPID_ERROR = 900010;private static Map<Integer, String> msgMap = new HashMap();private Integer code;static {msgMap.put(0, "成功");msgMap.put(900001, "加密明文文本非法");msgMap.put(900002, "加密时间戳参数非法");msgMap.put(900003, "加密随机字符串参数非法");msgMap.put(900005, "签名不匹配");msgMap.put(900006, "签名计算失败");msgMap.put(900004, "不合法的aes key");msgMap.put(900007, "计算加密文字错误");msgMap.put(900008, "计算解密文字错误");msgMap.put(900009, "计算解密文字长度不匹配");msgMap.put(900010, "计算解密文字corpid不匹配");}public Integer getCode() {return this.code;}public DingTalkEncryptException(Integer exceptionCode) {super((String)msgMap.get(exceptionCode));this.code = exceptionCode;}}static {try {Security.setProperty("crypto.policy", "limited");RemoveCryptographyRestrictions();} catch (Exception var1) {}}private static void RemoveCryptographyRestrictions() throws Exception {Class<?> jceSecurity = getClazz("javax.crypto.JceSecurity");Class<?> cryptoPermissions = getClazz("javax.crypto.CryptoPermissions");Class<?> cryptoAllPermission = getClazz("javax.crypto.CryptoAllPermission");if (jceSecurity != null) {setFinalStaticValue(jceSecurity, "isRestricted", false);PermissionCollection defaultPolicy = (PermissionCollection)getFieldValue(jceSecurity, "defaultPolicy", (Object)null, PermissionCollection.class);if (cryptoPermissions != null) {Map<?, ?> map = (Map)getFieldValue(cryptoPermissions, "perms", defaultPolicy, Map.class);map.clear();}if (cryptoAllPermission != null) {Permission permission = (Permission)getFieldValue(cryptoAllPermission, "INSTANCE", (Object)null, Permission.class);defaultPolicy.add(permission);}}}private static Class<?> getClazz(String className) {Class clazz = null;try {clazz = Class.forName(className);} catch (Exception var3) {}return clazz;}private static void setFinalStaticValue(Class<?> srcClazz, String fieldName, Object newValue) throws Exception {Field field = srcClazz.getDeclaredField(fieldName);field.setAccessible(true);Field modifiersField = Field.class.getDeclaredField("modifiers");modifiersField.setAccessible(true);modifiersField.setInt(field, field.getModifiers() & -17);field.set((Object)null, newValue);}private static <T> T getFieldValue(Class<?> srcClazz, String fieldName, Object owner, Class<T> dstClazz) throws Exception {Field field = srcClazz.getDeclaredField(fieldName);field.setAccessible(true);return dstClazz.cast(field.get(owner));}
}

DingDingController

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;import java.util.Map;/*** @Description 钉钉控制类* @Author JunHao Huang* @Date 2023/5/31 18:21*/
@RestController
@RequestMapping("dingding")
public class DingDingController {/*** 钉钉事件订阅token*/@Value("${dingtalk.app.token}")private String token;/*** 钉钉事件订阅aes_key*/@Value("${dingtalk.app.encodingAesKey}")private String encodingAesKey;/*** 钉钉程序对应的AppKey*/@Value("${dingtalk.app.appKey}")private String appKey;@PostMapping("/callBack")@ApiOperationSupport(order = 1)@ApiOperation(value = "测试回调", notes = "测试回调")@SneakyThrowspublic Map<String, String> callBack(@RequestParam(value = "msg_signature", required = false) String msg_signature,@RequestParam(value = "timestamp", required = false) String timeStamp,@RequestParam(value = "nonce", required = false) String nonce,@RequestBody(required = false) JSONObject json) {// 1. 从http请求中获取加解密参数// 2. 使用加解密类型// Constant.OWNER_KEY 说明:// 1、开发者后台配置的订阅事件为应用级事件推送,此时OWNER_KEY为应用的APP_KEY。// 2、调用订阅事件接口订阅的事件为企业级事件推送,//      此时OWNER_KEY为:企业的appkey(企业内部应用)或SUITE_KEY(三方应用)DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(token, encodingAesKey, appKey);String encryptMsg = json.getString("encrypt");String decryptMsg = callbackCrypto.getDecryptMsg(msg_signature, timeStamp, nonce, encryptMsg);// 3. 反序列化回调事件json数据JSONObject eventJson = JSON.parseObject(decryptMsg);String eventType = eventJson.getString("EventType");// 4. 根据EventType分类处理if ("check_url".equals(eventType)) {// 测试回调url的正确性} else if ("label_conf_modify".equals(eventType)) {// 修改角色或者角色组JSONArray postLabelList = eventJson.getJSONArray("PostLabelList");Long timeStamp1 = eventJson.getLong("TimeStamp");System.out.println(timeStamp);System.out.println(DateUtil.formatDateTime(new Date(Long.parseLong(timeStamp))));System.out.println(timeStamp1);System.out.println(DateUtil.formatDateTime(new Date(timeStamp1)));for (Object o : postLabelList) {JSONObject jsonObject = JSON.parseObject((String)o);PostLabel postLabel = JSONObject.toJavaObject(jsonObject, PostLabel.class);//以修改时间为准,而不是回调时间//用钉钉的修改时间比较数据库当条记录的返回的修改时间,如果数据库存储的修改时间大于本次修改时间,则不做任何操作System.out.println("修改角色或者角色组:"+postLabel.getName()+",修改时间"+ DateUtil.formatDateTime(new Date(timeStamp1)));}} else {// 添加其他已注册的}// 5. 返回success的加密数据Map<String, String> successMap = callbackCrypto.getEncryptedMap("success");return successMap;}
}

PostLabel

import lombok.Data;/*** @Description 钉钉角色或者组* @Author JunHao Huang* @Date 2023/6/1 14:16*/
@Data
public class PostLabel {private Boolean hidden;private String name;private Long id;
}

配置文件

dingtalk:app:token: 在事件与回调菜单,应用随机生成的tokenencodingAesKey: 在事件与回调菜单,应用随机生成的aes_keyappKey: 应用信息菜单,对应的AppKey

结尾

在这里插入图片描述

回调的数据结构->点击通讯录事件

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java对接钉钉 发送/更新 互动卡片消息

java对接钉钉 发送/更新 互动卡片消息 一、准备工作二、发送互动卡片消息三、更新互动消息卡片 先看下效果图 点击相应的积分按钮&#xff0c;就会给对应的申请人发送对应的积分&#xff08;对接到我们自己的系统发送&#xff09;&#xff0c;审核完之后&#xff0c;按钮变成已…

PHP-钉钉扫码登录对接第三方网站

这个小功能对于企业内部的一些自用后台网站还是很常用的&#xff0c;对接也相对简单&#xff0c;记录下对接流程。 开发文档&#xff1a;扫码登录第三方网站 - 钉钉开放平台 1.先登录钉钉开发者后台&#xff0c;创建扫码登录应用授权 开发者后台统一登录 - 钉钉统一身份认证 …

TED1984-2019全部中英对照演讲稿集 学习英文演讲写作的好帮手

TED1984-2019全部中英对照演讲稿集 学习英文演讲写作的好帮手 一、TED简介&#xff1a; TED&#xff08;指Technology, Entertainment, Design在英语中的缩写&#xff0c;即技术、娱乐、设计&#xff09;是美国的一家私有非营利机构&#xff0c;该机构以它组织的TED大会著称&…

那些会讲ppt的技术人有多爽?演讲的6个步骤

一场好的技术分享&#xff0c;可以用“有趣有料”四个字来形容&#xff0c;那么如何才能做到有趣又有料呢&#xff1f;结合我的经历&#xff0c;做了一些总结。 2015年&#xff0c;我出版《技术管理之巅》以后&#xff0c;先后收到QCon、CSDN、IT168等业界知名技术大会的邀请担…

关于计算机的英语演讲ppt模板,英语演讲ppt模板

目录 一、英语演讲ppt素材 ①.要简单点的 内容不限 但最好是寓言或者能给人点启示的东西 有意思就行 背景。 ②.要简单点的 内容不限 但最好是寓言或者能给人点启示的东西 有意思就行 背景。 ③.时间3-5分钟&#xff0c;主题无所谓&#xff0c;liuxiaoxuan89tom.com ④.Mans li…

自定义Msgbox密码登录

背景&#xff1a;虽然Excel是自带密码功能的&#xff0c;但是设置了密码以后&#xff0c;打开excel&#xff0c;是看不到excel的内容的。 如果想让使用者打开excel&#xff0c;能大概看到excel的内容 &#xff0c;但是会有弹窗&#xff0c;导致没法选择和更改数据&#xff0c;就…

Linux 系统密码策略设置

目录 Linux主要密码安全需求 1. 禁止使用旧密码 2.设置密码最短长度 3.设置密码复杂度 4.设置密码过期期限 Linux密码策略理论知识&#xff1a; 1、使用配置文件/etc/pam.conf 2、使用配置目录/etc/pam.d/ 讲怎么使用&#xff0c;后面有理论教程&#xff0c;先知其然再…

远程计算机guest密码更改了,怎么给guest设置密码

我们一般是在控制面板 - 用户帐户下启用来宾帐户&#xff0c;但我们可以在guest(供来宾访问计算机或访问域的内置帐户)下新建用户名和设置密码&#xff0c;方法简单&#xff0c;操作容易。下面是学习啦小编给大家整理的一些有关给guest设置密码的方法&#xff0c;希望对大家有帮…

计算机知识科普讲解大赛,谁是科普达人?2020年成都市科普讲解大赛拉开帷幕...

生活中无处不在的电磁波是怎样产生的呢&#xff1f; 被称为“汉代计算机”的一钩多综式提花织机与成都有什么关系&#xff1f; …… 近日&#xff0c;成都博物馆&#xff0c;40余家单位的70余名选手&#xff0c;通过4分钟参赛视频&#xff0c;亮相2020年成都市科普讲解大赛预赛…

chatgpt赋能python:Python预测分析:什么是预测分析?

Python预测分析&#xff1a;什么是预测分析&#xff1f; 随着数字化时代的到来&#xff0c;数据越来越多&#xff0c;数据分析的需求也随之增加。预测分析就是一种基于数据分析的技术&#xff0c;可以通过数据分析并应用统计模型&#xff0c;来预测未来发生的事件或趋势&#…

度盘搜失效?这款网盘搜索神器万万别错过!

奶糖猫来啦&#xff01;资源搜索一直以来都是需求非常大的一方面&#xff0c;总会有伙伴问我有没有这方面比较好用的软件。 之前很火的度盘搜失效了&#xff0c;后面也出现了一系列的资源搜索工具&#xff0c;但总存在一个致命的问题&#xff0c;资源普遍太老了&#xff0c;不能…

好用的网盘搜索引擎

最近&#xff0c;我们见到了许多安利网盘搜索引擎的推文&#xff0c;安利了很多网址和论坛。但经过答主亲测&#xff0c;许多被提到的网站&#xff0c;不是打不开链接&#xff0c;就是进去发现是广告&#xff0c;早已经被封停了&#xff0c;所以我们亲自测试了一些这方面的内容…

百度云盘搜索助手 V1.2(可查询提取码,带5个搜索引擎)

介绍&#xff1a; 云盘搜索助手功能强大&#xff0c;操作简单&#xff0c;使用后可以帮助用户更轻松快捷的搜索云盘资源。软件内置5个搜索引擎供您查资源&#xff0c;另外内置查询提取码功能。 网盘下载地址&#xff1a; http://www.bytepan.com/bNjeq9AKuVC 图片&#xff1a…

百度网盘_SEARCH

需求分析 我有一些资源网站&#xff0c;但是每次我需要资源的时候需要打开他们的网页&#xff0c;搜索再筛选我需要的网盘资源&#xff0c;这样的操作非常麻烦使用python模拟这些搜索操作&#xff0c;然后爬取我需要的百度网盘信息用python的Gui编程开发一个简单的界面 实现 …

使用ChatGPT进行个性化学习

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 在这篇文章中&#xff0c;您将发现 ChatGPT 作为机器学习和数据科学爱好者的个人导师的好处。特别是&#xff0c;您将学习 如何让ChatGPT引导你学习抽象代数如何让 ChatGPT 帮助您…

【对话ChatGPT】如何使用ChatGpt来学习和提问?

ChatGPT的不断发展和进步&#xff0c;我们需要工作中很多时候会用到ChatGPT&#xff0c;那么如何使用ChatGPT来解决我们工作中的问题呢&#xff1f; Q1 如何向ChatGPT提问&#xff0c;从而更快解决我们的问题&#xff1f; ChatGPT&#xff1a;以下是向ChatGPT提问的一些提示&am…

chatgpt赋能python:Python收费介绍

Python收费介绍 什么是Python? Python是一种高级的、解释性、面向对象、纯粹的动态语言&#xff0c;多用于快速应用程序开发、脚本编写、系统管理任务等。它有一个简单直观优美的语法&#xff0c;非常容易学习。 Python的收费形式 Python语言本身是免费的&#xff0c;任何…

微信「订阅号助手」 App 正式上线,请尽情吐槽!

说了一年&#xff0c;有可能做了更久。 现在微信「订阅号助手」 App 终于上线了&#xff0c;结果竟是这个。 打开微信订阅号助手 app&#xff0c;目前有邮箱登录和微信登陆两种方式&#xff0c;对于微信运营者来说&#xff0c;微信登陆无需输入账号密码&#xff0c;无疑是最省事…

易媒助手是自媒体一键式发布平台吗?

自媒体领域发展迅速&#xff0c;在当前这个大环境中&#xff0c;小伙伴们必须提高自己的内容发布效率&#xff0c;增加作品曝光量&#xff0c;才能在这个行业站得住脚。那么这里就要用到一些自媒体工具&#xff0c;比如像易媒助手这样的自媒体工具是一键发布内容平台吗&#xf…

自媒体营销工具-绑定自媒体账号一键分发到媒体平台,让运营高效省心

一、什么是自媒体营销 自媒体营销就是利用社会化网络、在线社区、博客、百科、短视频、微博、微信、今 日头条、百度、搜狐、凤凰、UC 等平台或者其他互联网协作平台和媒体来传播和发布 资讯&#xff0c;从而形成的营销、销售、公共关系处理和客户关系服务维护及开拓的一种方 …