有奖捉虫:办公协同&微信生态&物联网文档专题 HOT

功能描述

腾讯会议支持企业内管理员和成员行为记录的保存,您可以通过企业管理后台或 API 对管理员和成员行为进行审计,其中成员行为日志包括成员登录登出、会议管理、会议控制等行为的记录。

接入步骤


1



步骤1:创建企业级应用

企业自建应用的应用类型分为企业级和应用级类型。目前查询管理员操作日志仅支持企业级应用获取。
企业级类型:企业级可以获取到您企业账户下的所有数据,该类型的应用需要由管理员创建。




步骤2:生成公私钥对

通过 openssl 命令生成私钥,私钥需要企业自己维护。
生成私钥:openssl genrsa -out rsa_private_key.pem 1024,该指令支持1024和2048两种密钥长度。
生成公钥:openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem,通过此种方式生成的私钥是 pkcs1 格式,公钥是 pkcs8 格式。
私钥转 pkcs8 格式:openssl pkcs8 -topk8 -in rsa_private_key.pem -nocrypt -out pkcs8.pem

步骤3:上传公钥

接口描述:账户级接口,仅企业超级管理员可以上传公钥,通过接口将公钥上传给腾讯会议,用于对指定日志的加密。覆盖式接口。
请求方式:PUT
接口请求域名:
https://api.meeting.qq.com/v1/encryption/public-key
鉴权方式:JWT
限频:100次/min

输入参数

参数名称
参数类型
是否必须
参数描述
userid
String
操作人 ID。
enc_type
Integer
算法类型(目前仅支持 RSA 算法,填充模式为 RSA_PKCS1_PADDING)。
0:RSA;默认值为0。
public_key
String
公钥内容。
key_len
Integer
密钥长度,单位bit (对于 RSA 算法目前仅支持1024、2048)。
scene_type
Integer
加密场景(不同场景可以上传不同的公钥,来加密不同场景下的数据)。
0:管理员操作日志
1:企业成员行为日志

输出参数

{ }

步骤4:获取企业成员行为日志

接口描述:账户级接口,可以查询加密后的企业成员在腾会的行为日志信息。
请求方式:GET
接口请求域名:
https://api.meeting.qq.com/v1/log/user-log
鉴权方式:JWT
限频:100次/min

输入参数

参数名称
参数类型
是否必须
参数描述
start_time
QUERY
开始时间戳(秒级)。接口返回该时间戳所在当天的日志数据。
userid
QUERY
企业成员 ID。
event_type
QUERY
1:成员行为日志
2:用户登录登出日志
event_code
QUERY
事件 code。
meeting_id
QUERY
会议 ID。
operator_role
QUERY
操作者类型:
1:主持人
2:联席主持人
3:普通参会者
4:创建者
5:管理员
page
QUERY
页码,1-2000。默认为1。
page_size
QUERY
分页大小,50-1000。默认为50。

输出参数

参数名称
参数类型
参数描述
current_page
Integer
当前页码。
current_size
Integer
当前页条数。
total_page
Integer
总页数。
total_count
Integer
总条数。
log_list
Log 对象数组
日志列表(返回加密后的数据)。
enc_key
String
加密后的日志密钥。
Log 对象数组
参数名称
参数类型
参数描述
event_code
String
事件 code。
operator_id
String
操作人 ID。
operator_id_type
Integer
操作人类型:
1:userid
3:rooms_id
6:MRA 账号或 ip
operator_name
String
操作者名称。
operator_role
Integer
操作者角色:
1:主持人
2:联席主持人
3:普通参会者
4:创建者
5:管理员
instanceid
Integer
设备类型:
0:PSTN
1:PC
2:Mac
3:Android
4:iOS
5:Web
6:iPad
7:Android Pad
8:小程序
9:voip、sip 设备
10:linux
20:Rooms for Touch Windows
21:Rooms for Touch MacOS
22:Rooms for Touch Android
30:Controller for Touch Windows
32:Controller for Touch Android
33:Controller for Touch iOS
event_time
String
事件时间戳(秒级)。
event_details
Object
附录 定义。
meeting_id
String
会议 ID。

步骤5:使用私钥解密日志

通过接口获取到的 log_list 数据为通过对称加密后得到的数据。enc_key 为通过非对称加密后的对称密钥数据。您需要通过私钥解密出对称密钥的明文,再通过对称密钥解密日志列表拿到最终的日志明文。下面为解密数据的流程及代码 demo。



Python
Java
C++
Golang
import base64
from Crypto.Cipher import AES, PKCS1_v1_5
from Crypto.PublicKey import RSA
 
def decrypt_data(rsa_pri_key: str, enc_data: str, enc_key: str) -> str:
  
"""    
解密数据    
:param rsa_pri_key: PKCS#8格式的RSA私钥文件内容  
:param enc_data: 加密数据  
:param enc_key: 加密对称密钥   
:return: 解密后数据   
"""   
# 解密对称密钥    
aes_key = dec_enc_key(rsa_pri_key, enc_key)
    
# 解密数据    
dec_data = dec_enc_data(aes_key, enc_data)
    
return dec_data
 
def dec_enc_key(rsa_pri_key: str, enc_key: str) -> str:
  
"""   
解密对称密钥   
"""    
# base64解码对称密钥    
decode_key = base64.b64decode(enc_key)
    
# 私钥解密对称密钥    
rsa_key = RSA.import_key(rsa_pri_key)   
cipher = PKCS1_v1_5.new(rsa_key)   
aes_key = cipher.decrypt(decode_key, None)
    
if len(aes_key) != 32:        
raise ValueError("非256位对称密钥")
    
return aes_key.decode()
 
def pkcs7_unpadding(orig_data: str) -> str:
   
"""  
去除填充   
"""  
length = len(orig_data)   
unpadding = orig_data[-1]   
index = length - unpadding
  
if index < 0 or index >= length:        
raise ValueError("index out of range")
    
return orig_data[:index]

def dec_enc_data(aes_key: str, enc_data: str) -> str:
    
"""    
解密数据   
"""   
# 获取初始向量IV   
iv = aes_key[:16]
    
# base64解码数据   
decode_data = base64.b64decode(enc_data)
   
# 解密数据
cipher = AES.new(aes_key.encode(), AES.MODE_CBC, iv.encode())   
orig_data = cipher.decrypt(decode_data)
 
# 去除填充   
byte_dec_data = pkcs7_unpadding(orig_data)   
dec_data = byte_dec_data.decode('utf-8')
  
return dec_data

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
 
public class DecryptDemo {   
/**     
* decryptData 解密数据    
*     
* @param rsaPriKey PKCS#8格式的RSA私钥文件内容    
* @param encData 加密数据   
* @param encKey 加密对称密钥   
* @return decData 解密后数据    
*/    
public static String decryptData(String rsaPriKey, String encData, String encKey) throws Exception {
         
// 解密对称密钥       
String aesKey = decEncKey(rsaPriKey, encKey);
        
// 解密数据        
return decEncData(aesKey, encData);
   
}
  
/**     
* decEncKey 解密对称密钥     
* @param rsaPriKey    
* @param encKey     
* @return 对称密钥    
*/    
public static String decEncKey(String rsaPriKey, String encKey) throws Exception {
        
// base64解码对称密钥       
byte[] decodeKey = Base64.getDecoder().decode(encKey);
        
// 私钥解密对称密钥
Cipher cipher = Cipher.getInstance("RSA");        
PrivateKey pk = ReadPemKeyPair.loadPrivateKey(rsaPriKey);       
cipher.init(Cipher.DECRYPT_MODE, pk);       
String aesKey = new String(cipher.doFinal(decodeKey));
       
if (aesKey.length() != 32) {            
throw new Exception("非256位对称密钥");        
}        
return aesKey;    
} 
    
/**     
* decEncData 解密数据    
* @param aesKey    
* @param encData
* @return 解密后的明文数据     
*/    
public static String decEncData(String aesKey, String encData) throws Exception {
        
// 获取初始向量IV       
String iv = aesKey.substring(0, 16);
        
// base64解码数据       
byte[] decodeData = Base64.getDecoder().decode(encData);
       
// 解密数据       
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");       
IvParameterSpec params = new IvParameterSpec(iv.getBytes());      
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(aesKey.getBytes(), "AES"), params);

//        byte[] originData = pkcs7UnPadding(cipher.doFinal(decodeData));

//        return new String(originData, StandardCharsets.UTF_8);
       
return new String(cipher.doFinal(decodeData));
    
}
   
private static byte[] pkcs7UnPadding(byte[] originData) throws Exception {        
int length = originData.length;       
int unpadding = originData[length - 1];        
int index = length - unpadding;
        
if (index < 0 || index > length) {           
throw new Exception("index out of range");       
}
       
byte[] dst = new byte[index];
       
System.arraycopy(originData, 0, dst, 0, index);
        
return dst;
    
}
}
 
class ReadPemKeyPair {
   
private static String loadKey(String keyPem) throws IOException {       
BufferedReader brKey = new BufferedReader(new StringReader(keyPem));       
StringBuilder sb = new StringBuilder();       
String line;
     
while ((line = brKey.readLine())!= null) {           
if (!line.startsWith("-")) {                
sb.append(line);
            
}
        
}        
return sb.toString();
   
}    
public static PrivateKey loadPrivateKey(String privateKeyPem) throws GeneralSecurityException, IOException {       
// 读取 pem 中的 key        
String privateKeyStr = loadKey(privateKeyPem);        
byte [] pkcs8EncodedKeySpec = Base64.getDecoder().decode(privateKeyStr);       
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(pkcs8EncodedKeySpec);      
KeyFactory kf = KeyFactory.getInstance("RSA");       
return kf.generatePrivate(privSpec);   
}
   
public static PublicKey loadPublicKey(String publicKeyPem) throws GeneralSecurityException, IOException {       
// 读取 pem 中的 key        
String publicKeyStr = loadKey(publicKeyPem);       
X509EncodedKeySpec x509PubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));       
KeyFactory fact = KeyFactory.getInstance("RSA");       
return fact.generatePublic(x509PubKeySpec);   
}
}
#include <iostream>
#include <string>
#include <vector>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
 
// Base64解码
std::string base64Decode(const std::string &input) {    
BIO* bio_mem = BIO_new_mem_buf(input.data(), input.length());   
BIO* bio64 = BIO_new(BIO_f_base64());    
BIO_set_flags(bio64, BIO_FLAGS_BASE64_NO_NL);   
BIO_push(bio64, bio_mem);   
char buffer[1024];    
std::string result;    
int decoded_size;
    
while ((decoded_size = BIO_read(bio64, buffer, 1024)) > 0) {        
result.append(buffer, decoded_size);   
}
   
BIO_free_all(bio64);    
return result;
}
 
// 解密对称密钥
std::string decEncKey(const std::string &rsa_pri_key, const std::string &enc_key) {
    BIO *bio_mem = BIO_new_mem_buf(rsa_pri_key.data(), rsa_pri_key.size());
RSA *rsa = RSA_new();
rsa = PEM_read_bio_RSAPrivateKey(bio_mem, &rsa, nullptr, nullptr);
if (rsa == nullptr) {
return "";
}
    
// base64解码对称密钥   
std::string decoded_key = base64Decode(enc_key);
    
// 私钥解密对称密钥   
std::vector<unsigned char> decrypted_key(RSA_size(rsa));
   
int key_size = RSA_private_decrypt(decoded_key.size(), (const unsigned char *)decoded_key.data(), &decrypted_key[0], rsa, RSA_PKCS1_PADDING);
   
RSA_free(rsa);   
BIO_free(bio_mem);
   
return std::string(decrypted_key.begin(), decrypted_key.begin() + key_size);

}
 
// 去除填充
std::string pkcs7UnPadding(const std::string& orig_data) {
   
size_t length = orig_data.size();   
unsigned char unpadding = orig_data[length - 1];  
size_t index = length - unpadding;    
if (index < 0 || index >= length) {
throw std::runtime_error("index out of range");  
}
    
return orig_data.substr(0, index);
}
 
// 解密数据
std::string decEncData(const std::string &aes_key, const std::string &enc_data) {
   
// base64解码数据
std::string decoded_data = base64Decode(enc_data);
    
// 获取初始向量IV
std::string iv = aes_key.substr(0, 16);
    
// 解密数据
AES_KEY key;
    
if (AES_set_decrypt_key(reinterpret_cast<const unsigned char*>(aes_key.data()), 256, &key) < 0) {
throw std::runtime_error("failed to set AES decryption key");    
}
   
std::string orig_data(decoded_data.size(), '\\0');    
AES_cbc_encrypt(reinterpret_cast<const unsigned char*>(decoded_data.data()),
reinterpret_cast<unsigned char*>(&orig_data[0]),
                    
decoded_data.size(),
                    
&key,
                 
reinterpret_cast<unsigned char*>(&iv[0]),
                   
AES_DECRYPT);
    
std::string dec_data = pkcs7UnPadding(orig_data);
    
return dec_data;

}

 
/*
* DecryptData 解密数据 
* @param rsaPriKey PKCS#8格式的RSA私钥文件内容 
* @param encData 加密数据 
* @param encKey 加密对称密钥
* @return decData 解密后数据
*/

std::string DecryptData(const std::string &rsaPriKey, const std::string &encData, const std::string &encKey) {
// 解密对称密钥
std::string aesKey = decEncKey(rsaPriKey, encKey);
    
if (aesKey == "") {
        return "";    
}
// 解密数据
std::string decData = decEncData(aesKey, encData);
return decData;

}
package util
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
)
 
/* 
* DecryptData 解密数据 
* @param rsaPriKey PKCS#8格式的RSA私钥文件内容 
* @param encData 加密数据 
* @param encKey 加密对称密钥 
* @return decData 解密后数据 
* @return err 错误信息
*/

func DecryptData(rsaPriKey, encData, encKey string) (decData string, err error) {
// 解密对称密钥
aesKey, err := decEncKey(rsaPriKey, encKey)
if err != nil {
return "", err
}
// 解密数据
decData, err = decEncData(aesKey, encData)
if err != nil {
return "", err
}
return decData, nil

}
 
// decEncKey 解密对称密钥
func decEncKey(rsaPriKey, encKey string) (string, error) {
// base64解码对称密
decodeKey, err := base64.StdEncoding.DecodeString(string(encKey))
if err != nil {
return "", err
}
// 私钥解密对称密钥
pemBlk, _ := pem.Decode([]byte(rsaPriKey))
if pemBlk == nil {
return "", errors.New("非法PEM格式的RSA私钥文件")
}
pkInf, err := x509.ParsePKCS8PrivateKey(pemBlk.Bytes)
if err != nil {
return "", err
}

pk, ok := pkInf.(*rsa.PrivateKey)

if !ok {
return "", errors.New("非PKCS#8格式的RSA私钥文件")
}

byteAESKey, err := rsa.DecryptPKCS1v15(rand.Reader, pk, decodeKey)

if err != nil {
return "", err
}
aesKey := string(byteAESKey)
if len(aesKey) != 32 {
return "", errors.New("非256位对称密钥")
}

return aesKey, nil
}

 
// pkcs7UnPadding 去除填充

func pkcs7UnPadding(origData []byte) ([]byte, error) {

length := len(origData)
unpadding := int(origData[length-1])
index := length - unpadding

if index < 0 || index >= len(origData) {
return nil, errors.New("index out of range")
}

return origData[:index], nil
}

 
// decEncData 解密数据
func decEncData(aesKey, encData string) (string, error) {
// 获取初始向量IV
iv := aesKey[:16]
// base64解码数据
decodeData, err := base64.StdEncoding.DecodeString(string(encData))
if err != nil {
return "", err
}
// 解密数据
cipherBlk, err := aes.NewCipher([]byte(aesKey))
if err != nil {
return "", err
}

blockMode := cipher.NewCBCDecrypter(cipherBlk, []byte(iv))

origData := make([]byte, len(decodeData))

blockMode.CryptBlocks(origData, decodeData)

byteDecData, err := pkcs7UnPadding(origData)

if err != nil {
return "", err
}

decData := string(byteDecData)
return decData, nil
}
拿到日志明文为 json 格式,公参为每条日志都会返回的数据包括事件 code、事件时间、事件操作者等信息,私参 event_details 根据不同的事件返回的内容不同,您可以根据业务需要处理 event_details 对象。

附录

event_details 定义。

登录登出

事件名称
事件code
event_details对象定义
用户通过授权登录
user_login_by_authorize
{ "login_type": "", // string,登录类型 "client_ver": "", // string,客户端版本号 "client_ip": "", //string,客户端日志ip "server_ip": ""//string,接入机ip }
用户通过手机号登录
user_login_by_phone
{ "login_type": "", // string,登录类型 "client_ver": "", // string,客户端版本号 "client_ip": "", //string,客户端日志ip "server_ip": ""//string,接入机ip }
个人用户退出登录
user_logout
{ "logout_type": "", // string,登出类型 "client_ver": "", // string,客户端版本号 }

其他

事件名称
事件code
event_details对象定义
关闭麦克风
mute
{ "audio_device_name":"", //string, 音频设备名称 "has_camera":, //boolean, 用户是否带有摄像头 "has_mic": //boolean, 用户是否带有MIC }
开启麦克风
unmute
开启共享音频
open_share_audio
{ "allow_record": , //boolean, 主持人全局权限 "is_use_share_screen": , //boolean,当前是否开共享屏幕 "permission": , //boolean,是否允许协作 "share_type": //int, 共享类型 0=屏幕共享;1=白板 }
关闭共享音频
close_share_audio
打开共享屏幕或白板
open_share_screen
关闭共享屏幕或白板
close_share_screen
暂停共享屏幕或白板
pause_share_screen
开启摄像头
open_video
{ "has_camera": , // boolean, 用户是否带有摄像头 "video_device_name":"" // string,视频设备名称 }
关闭摄像头
close_video
举手
raise_hand
{ "action_type": //int, 0=手放下,1=举手: }
关闭字幕
close_subtitle
{ "source_language": ,//int,声源语言 1-中文,2-英文,3-日语 "transcript_language": ,//int,翻译语言 0-不翻译,1-中文,2-英文 }
开启字幕
open_subtitle
关闭云录制
close_cloud_record
{}

开启云录制
open_cloud_record
关闭本地录制
close_local_record
{ "audio_device_name""", // string, 音频设备名称 "video_device_name":"", // string, 视频设备名称 "is_use_share_screen":, // boolean, 当前是否开共享屏幕 "local_record_status":"", // string, 与会成员录制状态,0-关闭,1-开启 }
开启本地录制
open_local_record
创建会议
create_meeting
{ "creator_time_zone":"",//string,创建会议时区 "meeting_id":"", //string,会议号码 "begin_time": "", //string 会议开始时间戳 "end_time":"", //string 结束时间戳 "meeting_type": , //int,会议类型,0-一次性会议,1-周期性会议 "create_mode": , //int,会议模式 0-快速会议,1-预约会议 "allow_in_before_host": , // boolean,允许成员在主持人进会前加入 "auto_in_waiting_room": , // boolean,是否开启等候室 "auto_record_type": , // int,自动开启录制类型 0-云录制,1-本地录制 "location":"", //string,会议地点 "meeting_lock":, //boolean,会议锁定 "mute_on_join":, //boolean,入会时静音 "mute_on_upperxman_join":, //boolean,入会超x人时自动静音 "si_enable":, // boolean,是否开启同传 "subject":"", // string,会议主题 "time_zone_id":"", //string, 时区ID "watermark": //boolean, 是否开启水印 }
取消会议
cancel_meeting
{}
修改会议
edit_meeting
{ "creator_time_zone":"", //string,创建会议时区 "meeting_id":"", //string,会议号码 "begin_time": "", // string,会议开始时间 "end_time":"", // string,结束时间 "allow_in_before_host": , //boolean,允许成员在主持人进会前加入 "auto_in_waiting_room": , //boolean,是否开启等候室 "auto_record_type": , //int,自动开启录制类型,0-云录制,1-本地录制 "location":"", //string,会议地点 "meeting_lock":, //boolean,会议锁定 "mute_on_join":, //boolean,入会时静音 "mute_on_upperxman_join":, //boolean,入会超x人时自动静音 "si_enable":, // boolean,是否开启同传 "subject":"", // string,会议主题 "time_zone_id":"" //string, 时区ID }
上报修改的字段及其内容。
删除会议
delete_meeting
{}
将成员移出会议
kick_out_of_meeting
{ "to_operator_id":"",//string,被操做人ID "to_operator_id_type": ,//int, 被操作人ID类型,1-userid(同企业普通用户返回),3-rooms_id(同企业Rooms返回),4-ms_open_id(非同企业成员),6-注册账号或设备IP(同企业MRA) "to_operator_name":"",//string,被操作者会中昵称 "allow_rejoin":// boolean, 是否允许重新加入 }
加入会议
join_meeting_by_media_backend
{ "nick_name":"", // string, 入房用户昵称 "instance_id": , //int, 设备类型 }
离开会议
leave_meeting_by_media_backend_filter
{ "nick_name":"", // string,入房用户昵称 "instance_id": , //int, 设备类型 "join_meeting_time":"", //string,入会时间,毫秒时间戳 "leave_reason":"" //string, 离开原因 }
打开远程控制
open_remote_control
{ "is_use_share_screen": //boolean, 当前是否开共享屏幕 }
关闭远程控制
close_remote_control
{ "is_use_share_screen"://boolean,当前是否开共享屏幕 }
设置主持人
set_host
{ "to_operator_id":"",//string,被操作者ID "to_operator_id_type": ,//int, 被操作人ID类型,1-userid(同企业普通用户返回),3-rooms_id(同企业Rooms返回),4-ms_open_id(非同企业成员),6-注册账号或设备IP(同企业MRA) "to_operator_name":"",//string,被操作者会中昵称 }
设置联席主持人
set_joint_host
取消联席主持人角色
cancel_joint_host
回收主持人角色
cancel_set_host
{}
全体静音
all_mute
{}
全体静音且允许自己解除静音
all_mute_allow_unmute
解除全体静音
all_unmute
设置成员静音
mute_user
{ "to_operator_id":"",//string,被操作者ID "to_operator_id_type":,//int, 被操作人ID类型,1-userid(同企业普通用户返回),3-rooms_id(同企业Rooms返回),4-ms_open_id(非同企业成员),6-注册账号或设备IP(同企业MRA) "to_operator_name":"",//string,被操作者会中昵称 }
解除成员静音
unmute_user
开始分组讨论
begin_group_discussion
{ "breakout_room_num":1,//分组个数 "breakout_room_groups":[ { "breakout_room_name":"", "breakout_room_id":"", "breakout_room_member":[
{"ms_open_id":"xxxx"}
] //被操作ms_open_id数组 }
] }
结束分组讨论
end_group_discussion
打开互动批注
open_interactive_annotations
{}
关闭互动批注
close_interactive_annotations
将成员移至等候室
move_to_waiting_room
{ "to_operator_list": [ { "to_operator_id": "operator1", //string,被操作者ID "to_operator_id_type": 1,//int, 被操作人ID类型,1-userid(同企业普通用户返回),3-rooms_id(同企业Rooms返回),4-ms_open_id(非同企业成员),6-注册账号或设备IP(同企业MRA) "to_operator_name": "Operator 1"//string,被操作者会中昵称 }, { "to_operator_id": "operator2", "to_operator_id_type": 3, "to_operator_name": "Operator 2" }] }
开启等候室
open_waiting_room
{}
打开水印
open_watermark
{}
关闭水印
close_watermark
{}
修改会中名称
change_name
{ "to_operator_id":"",//string,被操作者ID "to_operator_id_type":,//int, 被操作人ID类型,1-userid(同企业普通用户返回),3-rooms_id(同企业Rooms返回),4-ms_open_id(非同企业成员),6-注册账号或设备IP(同企业MRA) "change_name_type":, //int,修改昵称类型 1=自己改,2=别人改 "old_nick_name":"", //string,被操作人修改后的nick_name "new_nick_name":"" //string,被操作人修改前的nick_name }
锁定会议
lock_meeting
{}
解除锁定会议
unlock_meeting
{}
结束会议
dismiss_meeting
{ "current_meeting_number":,//int, 当前会议人数 "retrieve_code":, //boolean,是否回收会议号 "force_dismiss_meeting":, //boolean,是否强制解散会议 "is_use_share_screen": //boolean,当前是否开共享屏幕(解散会议实时状态) }