Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+879
View File
@@ -0,0 +1,879 @@
package imclient
import (
"bytes"
"crypto/aes"
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
"91porn-server/common/crypt/ecb"
"91porn-server/common/log"
"github.com/moul/http2curl"
)
const (
MessageTypeText = 0
MessageTypeImage = 2
)
type Config struct {
Enable bool
BaseURL string
MerchantCode string
TenantCode string
AppKey string
ClientID string
ClientSecret string
SignKey string
AESKey string
EnableSign bool
EncryptTimestamp bool
OS string
OSType string
BusinessType string
Language string
TokenTTL int64
RequestTimeoutSeconds int
}
type Client struct {
cfg Config
httpClient *http.Client
}
type Response struct {
Code int `json:"code"`
Data json.RawMessage `json:"data"`
Msg string `json:"msg"`
Message string `json:"message"`
Result string `json:"result"`
}
type Attachment struct {
URL string `json:"url,omitempty"`
ThumbURL string `json:"thumbUrl,omitempty"`
Type string `json:"type,omitempty"`
Size int64 `json:"size,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
Length int64 `json:"length,omitempty"`
FileName string `json:"fileName,omitempty"`
}
type AppTokenRequest struct {
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
OSType string `json:"osType"`
TTL int64 `json:"ttl,omitempty"`
}
type UserTokenRequest struct {
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
UserID int64 `json:"userId"`
OSType string `json:"osType"`
TTL int64 `json:"ttl,omitempty"`
}
type RegisterRequest struct {
ThirdPartyID string `json:"thirdPartyId"`
Password string `json:"password"`
Nickname string `json:"nickname,omitempty"`
Avatar string `json:"avatar,omitempty"`
}
type UpdateBaseInfoRequest struct {
UserID int64 `json:"userId"`
Name string `json:"name,omitempty"`
ImgURL string `json:"imgUrl,omitempty"`
Signature string `json:"signature,omitempty"`
}
type UserBaseInfo struct {
ID int64 `json:"id"`
UserPhone string `json:"userPhone"`
Name string `json:"name"`
Sex int `json:"sex"`
ImgURL string `json:"imgUrl"`
Enable int `json:"enable"`
CreateTime int64 `json:"createTime"`
Signature string `json:"signature"`
}
type UpdatePasswordRequest struct {
UserID int64 `json:"userId"`
Pwd string `json:"pwd"`
Code string `json:"code,omitempty"`
}
type UpdateHeadImgRequest struct {
UserID int64 `json:"userId"`
ImgURL string `json:"imgUrl"`
}
type SetOnlineStatusRequest struct {
UserID int64 `json:"userId"`
ShowOnlineStatus bool `json:"showOnlineStatus"`
}
type BatchOnlineStatusRequest struct {
UserIDs []int64 `json:"userIds"`
}
type OnlineStatus struct {
UserID int64 `json:"userId"`
UserName string `json:"userName"`
Visible bool `json:"visible"`
Online bool `json:"online"`
}
type Friend struct {
FriendID int64 `json:"friendId"`
Remark string `json:"remark"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
Status int `json:"status"`
}
type AddFriendRequest struct {
UserID int64 `json:"userId"`
FriendID int64 `json:"friendId"`
Message string `json:"message,omitempty"`
}
type DirectAddFriendRequest struct {
UserID int64 `json:"userId"`
FriendID int64 `json:"friendId"`
Message string `json:"message,omitempty"`
Archive bool `json:"archive,omitempty"`
}
type HandleFriendRequest struct {
RequestID int64 `json:"requestId"`
UserID int64 `json:"userId"`
}
type PendingFriendRequest struct {
ID int64 `json:"id"`
FriendID int64 `json:"friendId"`
Message string `json:"message"`
Status int `json:"status"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
HandleTime int64 `json:"handleTime"`
ExpireTime int64 `json:"expireTime"`
}
type AvailableFriend struct {
UserID int64 `json:"userId"`
FriendID int64 `json:"friendId"`
ConvID string `json:"convId"`
Remark string `json:"remark"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
DisturbStatus int `json:"disturbStatus"`
Archive int `json:"archive"`
}
type SetFriendArchiveRequest struct {
UserID int64 `json:"userId"`
FriendID int64 `json:"friendId"`
Archive bool `json:"archive"`
}
type SendMessageRequest struct {
SenderID int64 `json:"senderId"`
ReceiverID int64 `json:"receiverId"`
Content string `json:"content"`
MessageType int `json:"messageType"`
ExtInfo string `json:"extInfo,omitempty"`
ChannelType string `json:"channelType,omitempty"`
Attachment *Attachment `json:"attachment,omitempty"`
}
type BatchSendMessageRequest struct {
SenderID int64 `json:"senderId"`
ReceiverIDSet []int64 `json:"receiverIdSet"`
Content string `json:"content"`
MessageType int `json:"messageType"`
ExtInfo string `json:"extInfo,omitempty"`
ChannelType string `json:"channelType,omitempty"`
Attachment *Attachment `json:"attachment,omitempty"`
}
type OnlinePassthroughRequest struct {
SenderID int64 `json:"senderId,omitempty"`
ReceiverIDSet []int64 `json:"receiverIdSet"`
PassthroughType string `json:"passthroughType,omitempty"`
Content string `json:"content,omitempty"`
ExtInfo string `json:"extInfo,omitempty"`
ChannelType string `json:"channelType,omitempty"`
}
type AppPassthroughRequest struct {
SenderID int64 `json:"senderId,omitempty"`
PassthroughType string `json:"passthroughType,omitempty"`
Content string `json:"content,omitempty"`
ExtInfo string `json:"extInfo,omitempty"`
ChannelType string `json:"channelType,omitempty"`
}
type PassthroughResult struct {
MessageID string `json:"messageId"`
MessageType int `json:"messageType"`
ReceiverCount int `json:"receiverCount"`
CreatedAt int64 `json:"createdAt"`
}
type MessageSendResult struct {
MessageID string `json:"messageId"`
ConvID string `json:"convId"`
Seq int64 `json:"seq"`
CreatedAt int64 `json:"createdAt"`
}
type HistoryMessageRequest struct {
UserID1 int64 `json:"userId1"`
UserID2 int64 `json:"userId2"`
StartTime int64 `json:"startTime,omitempty"`
EndTime int64 `json:"endTime,omitempty"`
StartSeq int64 `json:"startSeq,omitempty"`
EndSeq int64 `json:"endSeq,omitempty"`
Direction string `json:"direction,omitempty"`
Size int `json:"size,omitempty"`
}
type MessageDetail struct {
MessageID string `json:"messageId"`
SenderID int64 `json:"senderId"`
ReceiverID int64 `json:"receiverId"`
Content string `json:"content"`
MessageType string `json:"messageType"`
Seq int64 `json:"seq"`
CreatedAt int64 `json:"createdAt"`
}
type ModifyMessageRequest struct {
MessageID string `json:"messageId"`
ConvID string `json:"convId"`
SenderID int64 `json:"senderId"`
ReceiverID int64 `json:"receiverId"`
ModifierID int64 `json:"modifierId"`
Content string `json:"content,omitempty"`
ExtInfo string `json:"extInfo,omitempty"`
}
type HideInboxRequest struct {
UserID int64 `json:"userId"`
ConvID string `json:"convId"`
StartSeq int64 `json:"startSeq"`
EndSeq int64 `json:"endSeq"`
}
func New(cfg Config) *Client {
timeout := cfg.RequestTimeoutSeconds
if timeout <= 0 {
timeout = 10
}
return &Client{
cfg: cfg,
httpClient: &http.Client{
Timeout: time.Duration(timeout) * time.Second,
},
}
}
func (c *Client) Enabled() bool {
return c.DisabledReason() == ""
}
// DisabledReason 返回 SDK 不可用的具体原因(未开启 / 缺失的配置项);可用时返回 ""。
func (c *Client) DisabledReason() string {
if c == nil {
return "im client is nil"
}
if !c.cfg.Enable {
return "config Enable=false"
}
var missing []string
if c.cfg.BaseURL == "" {
missing = append(missing, "BaseURL")
}
if c.cfg.MerchantCode == "" {
missing = append(missing, "MerchantCode")
}
if c.tenantCode() == "" {
missing = append(missing, "TenantCode/AppKey")
}
if len(missing) > 0 {
missingStr := "missing config: " + strings.Join(missing, ", ")
log.Error(missingStr)
return missingStr
}
return ""
}
func (c *Client) AppToken() (string, error) {
req := AppTokenRequest{
ClientID: c.cfg.ClientID,
ClientSecret: c.cfg.ClientSecret,
OSType: c.osType(),
TTL: c.tokenTTL(),
}
return c.postString("/authenticate/token.e", "", req)
}
func (c *Client) UserToken(userID int64) (string, error) {
req := UserTokenRequest{
ClientID: c.cfg.ClientID,
ClientSecret: c.cfg.ClientSecret,
UserID: userID,
OSType: c.osType(),
TTL: c.tokenTTL(),
}
return c.postString("/authenticate/user/token.e", "", req)
}
func (c *Client) Register(req RegisterRequest, token string) (int64, error) {
var userID int64
if err := c.postData("/user/register", token, req, &userID); err != nil {
return 0, err
}
return userID, nil
}
func (c *Client) UpdateBaseInfo(req UpdateBaseInfoRequest, token string) error {
var ok bool
return c.postData("/user/updateBaseInfo", token, req, &ok)
}
func (c *Client) UserBaseInfo(userID int64, token string) (*UserBaseInfo, error) {
query := url.Values{}
query.Set("userId", strconv.FormatInt(userID, 10))
var info UserBaseInfo
if err := c.getData("/user/baseUserInfo", token, query, nil, &info); err != nil {
return nil, err
}
return &info, nil
}
func (c *Client) UpdatePassword(req UpdatePasswordRequest, token string) error {
return c.postData("/user/updatePwd", token, req, nil)
}
func (c *Client) UpdateHeadImg(req UpdateHeadImgRequest, token string) error {
var ok bool
return c.postData("/user/updateHeadImg", token, req, &ok)
}
func (c *Client) SetOnlineStatus(req SetOnlineStatusRequest, token string) error {
var ok bool
return c.postData("/user/onlineStatus/set", token, req, &ok)
}
func (c *Client) BatchOnlineStatus(req BatchOnlineStatusRequest, token string) ([]OnlineStatus, error) {
var statuses []OnlineStatus
if err := c.postData("/user/onlineStatus/batch", token, req, &statuses); err != nil {
return nil, err
}
return statuses, nil
}
func (c *Client) FriendList(userID, now int64, token string) ([]Friend, error) {
query := url.Values{}
query.Set("userId", strconv.FormatInt(userID, 10))
if now > 0 {
query.Set("now", strconv.FormatInt(now, 10))
}
headers := map[string]string{
"userId": strconv.FormatInt(userID, 10),
}
var friends []Friend
if err := c.getData("/friend/list", token, query, headers, &friends); err != nil {
return nil, err
}
return friends, nil
}
func (c *Client) AddFriend(req AddFriendRequest, token string) (int64, error) {
var requestID int64
if err := c.postData("/friend/add", token, req, &requestID); err != nil {
return 0, err
}
return requestID, nil
}
func (c *Client) DirectAddFriend(req DirectAddFriendRequest, token string) error {
var ok bool
return c.postData("/friend/add/direct", token, req, &ok)
}
func (c *Client) ConfirmFriend(req HandleFriendRequest, token string) error {
var ok bool
return c.postData("/friend/confirm", token, req, &ok)
}
func (c *Client) RejectFriend(req HandleFriendRequest, token string) error {
var ok bool
return c.postData("/friend/reject", token, req, &ok)
}
func (c *Client) DeleteFriend(userID, friendID int64, token string) error {
query := url.Values{}
query.Set("userId", strconv.FormatInt(userID, 10))
query.Set("friendId", strconv.FormatInt(friendID, 10))
var ok bool
return c.getData("/friend/delete", token, query, nil, &ok)
}
func (c *Client) DeleteAllFriends(userID int64, token string) error {
query := url.Values{}
query.Set("userId", strconv.FormatInt(userID, 10))
var ok bool
return c.getData("/allFriend/delete", token, query, nil, &ok)
}
func (c *Client) PendingFriendRequests(userID, now int64, token string) ([]PendingFriendRequest, error) {
query := url.Values{}
query.Set("userId", strconv.FormatInt(userID, 10))
if now > 0 {
query.Set("now", strconv.FormatInt(now, 10))
}
var requests []PendingFriendRequest
if err := c.getData("/friend/pending", token, query, nil, &requests); err != nil {
return nil, err
}
return requests, nil
}
func (c *Client) AvailableFriends(userID int64, token string) ([]AvailableFriend, error) {
query := url.Values{}
query.Set("userId", strconv.FormatInt(userID, 10))
var friends []AvailableFriend
if err := c.getData("/friend/available/list", token, query, nil, &friends); err != nil {
return nil, err
}
return friends, nil
}
func (c *Client) SetFriendArchive(req SetFriendArchiveRequest, token string) error {
var ok bool
return c.postData("/friend/archive", token, req, &ok)
}
func (c *Client) SendMessage(req SendMessageRequest, token string) error {
_, err := c.SendMessageWithResult(req, token)
return err
}
func (c *Client) SendMessageWithResult(req SendMessageRequest, token string) (*MessageSendResult, error) {
if req.ExtInfo == "" {
req.ExtInfo = "{}"
}
if req.ChannelType == "" {
req.ChannelType = "UNKNOW"
}
var result MessageSendResult
if err := c.postData("/message/send", token, req, &result); err != nil {
return nil, err
}
return &result, nil
}
func (c *Client) BatchSendMessage(req BatchSendMessageRequest, token string) error {
_, err := c.BatchSendMessageWithResult(req, token)
return err
}
func (c *Client) BatchSendMessageWithResult(req BatchSendMessageRequest, token string) (json.RawMessage, error) {
if req.ExtInfo == "" {
req.ExtInfo = "{}"
}
if req.ChannelType == "" {
req.ChannelType = "UNKNOW"
}
var data json.RawMessage
if err := c.postData("/message/send/batch", token, req, &data); err != nil {
return nil, err
}
return data, nil
}
func (c *Client) HistoryMessages(req HistoryMessageRequest, token string) ([]MessageDetail, error) {
var messages []MessageDetail
if err := c.postData("/message/history", token, req, &messages); err != nil {
return nil, err
}
return messages, nil
}
func (c *Client) MessageDetail(messageID, convID, token string) (*MessageDetail, error) {
query := url.Values{}
query.Set("messageId", messageID)
query.Set("convId", convID)
var detail MessageDetail
if err := c.getData("/message/detail", token, query, nil, &detail); err != nil {
return nil, err
}
return &detail, nil
}
func (c *Client) ModifyMessage(req ModifyMessageRequest, token string) (string, error) {
var messageID string
if err := c.postData("/message/modify", token, req, &messageID); err != nil {
return "", err
}
return messageID, nil
}
func (c *Client) HideInbox(req HideInboxRequest, token string) (int64, error) {
var count int64
if err := c.postData("/message/inbox/hide", token, req, &count); err != nil {
return 0, err
}
return count, nil
}
func (c *Client) SendOnlinePassthrough(req OnlinePassthroughRequest, token string) (*PassthroughResult, error) {
if len(req.ReceiverIDSet) == 0 {
return nil, errors.New("receiverIdSet is empty")
}
normalizePassthrough(&req.PassthroughType, &req.ExtInfo, &req.ChannelType)
var result PassthroughResult
if err := c.postData("/message/passthrough/send", token, req, &result); err != nil {
return nil, err
}
return &result, nil
}
func (c *Client) SendAppPassthrough(req AppPassthroughRequest, token string) (*PassthroughResult, error) {
normalizePassthrough(&req.PassthroughType, &req.ExtInfo, &req.ChannelType)
var result PassthroughResult
if err := c.postData("/message/passthrough/send/batch/all", token, req, &result); err != nil {
return nil, err
}
return &result, nil
}
func normalizePassthrough(passthroughType, extInfo, channelType *string) {
if *passthroughType == "" {
*passthroughType = "AD_NOTIFY"
}
if *extInfo == "" {
*extInfo = "{}"
}
if *channelType == "" {
*channelType = "UNKNOW"
}
}
func marshalRequestBody(data any) ([]byte, error) {
switch req := data.(type) {
case AppTokenRequest:
return marshalOrderedJSON(map[string]any{
"clientId": req.ClientID,
"clientSecret": req.ClientSecret,
"osType": req.OSType,
"ttl": req.TTL,
})
case UserTokenRequest:
return marshalOrderedJSON(map[string]any{
"clientId": req.ClientID,
"clientSecret": req.ClientSecret,
"osType": req.OSType,
"ttl": req.TTL,
"userId": req.UserID,
})
case RegisterRequest:
fields := map[string]any{
"password": req.Password,
"thirdPartyId": req.ThirdPartyID,
}
if req.Nickname != "" {
fields["nickname"] = req.Nickname
}
if req.Avatar != "" {
fields["avatar"] = req.Avatar
}
return marshalOrderedJSON(fields)
default:
return json.Marshal(data)
}
}
func marshalOrderedJSON(fields map[string]any) ([]byte, error) {
keys := make([]string, 0, len(fields))
for key, value := range fields {
if value == nil {
continue
}
if s, ok := value.(string); ok && s == "" {
continue
}
if n, ok := value.(int64); ok && n <= 0 {
continue
}
keys = append(keys, key)
}
sort.Strings(keys)
var buf bytes.Buffer
buf.WriteByte('{')
for i, key := range keys {
if i > 0 {
buf.WriteByte(',')
}
keyJSON, err := json.Marshal(key)
if err != nil {
return nil, err
}
valueJSON, err := json.Marshal(fields[key])
if err != nil {
return nil, err
}
buf.Write(keyJSON)
buf.WriteByte(':')
buf.Write(valueJSON)
}
buf.WriteByte('}')
return buf.Bytes(), nil
}
func (c *Client) postString(path, token string, data any) (string, error) {
var out string
if err := c.postData(path, token, data, &out); err != nil {
return "", err
}
return out, nil
}
func (c *Client) postData(path, token string, data any, out any) error {
if !c.Enabled() {
return errors.New("im client is disabled")
}
body, err := marshalRequestBody(data)
if err != nil {
return err
}
resp, err := c.do(http.MethodPost, path, token, nil, nil, body)
if err != nil {
return err
}
return decodeResponseData(resp, out)
}
func (c *Client) getData(path, token string, query url.Values, headers map[string]string, out any) error {
if !c.Enabled() {
return errors.New("im client is disabled")
}
resp, err := c.do(http.MethodGet, path, token, query, headers, nil)
if err != nil {
return err
}
return decodeResponseData(resp, out)
}
func decodeResponseData(resp *Response, out any) error {
if out == nil || len(resp.Data) == 0 || bytes.Equal(resp.Data, []byte("null")) {
return nil
}
return json.Unmarshal(resp.Data, out)
}
func (c *Client) do(method, path, token string, query url.Values, headers map[string]string, body []byte) (*Response, error) {
fullURL, queryString := c.endpoint(path, query)
req, err := http.NewRequest(method, fullURL, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
req.Header.Set("X-Merchant-Code", c.cfg.MerchantCode)
req.Header.Set("X-App-Key", c.appKey())
req.Header.Set("X-Client-Id", c.cfg.ClientID)
if token != "" {
req.Header.Set("token", token)
}
for k, v := range headers {
req.Header.Set(k, v)
}
for k, v := range c.baseHeaders(queryString, body) {
req.Header.Set(k, v)
}
start := time.Now()
httpResp, err := c.httpClient.Do(req)
if err != nil {
log.Warn("im sdk request error", log.Any("method", method), log.Any("path", path), log.Any("costMs", time.Since(start).Milliseconds()), log.Any("curl", requestCurl(req, body)), log.E(err))
return nil, err
}
defer httpResp.Body.Close()
respBody, err := io.ReadAll(httpResp.Body)
cost := time.Since(start)
if err != nil {
log.Warn("im sdk read body error", log.Any("method", method), log.Any("path", path), log.Any("costMs", cost.Milliseconds()), log.E(err))
return nil, err
}
log.Info("im sdk request", log.Any("method", method), log.Any("path", path), log.Any("status", httpResp.StatusCode), log.Any("costMs", cost.Milliseconds()))
if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
log.Warn("im sdk http status error", log.Any("method", method), log.Any("path", path), log.Any("status", httpResp.StatusCode), log.Any("curl", requestCurl(req, body)), log.Any("resp", string(respBody)))
return nil, apiErrorFromResponse(httpResp.StatusCode, respBody)
}
var resp Response
if err = json.Unmarshal(respBody, &resp); err != nil {
log.Warn("im sdk resp unmarshal error", log.Any("method", method), log.Any("path", path), log.Any("curl", requestCurl(req, body)), log.Any("resp", string(respBody)), log.E(err))
return nil, err
}
if resp.Code != 0 {
log.Warn("im sdk api error", log.Any("method", method), log.Any("path", path), log.Any("code", resp.Code), log.Any("curl", requestCurl(req, body)), log.Any("resp", string(respBody)))
return nil, &APIError{
StatusCode: httpResp.StatusCode,
Code: resp.Code,
Message: firstNonEmpty(resp.Message, resp.Msg, resp.Result),
Body: respBody,
}
}
return &resp, nil
}
// requestCurl 把请求还原成 curl 命令字符串,用于报错时打印复现。
// body 为请求体原始字节:Do 已消费 req.Body,这里用 body 重新填充再生成。
func requestCurl(req *http.Request, body []byte) string {
req.Body = io.NopCloser(bytes.NewReader(body))
cmd, err := http2curl.GetCurlCommand(req)
if err != nil {
return ""
}
return cmd.String()
}
func apiErrorFromResponse(statusCode int, body []byte) *APIError {
resp := Response{}
_ = json.Unmarshal(body, &resp)
return &APIError{
StatusCode: statusCode,
Code: resp.Code,
Message: firstNonEmpty(resp.Message, resp.Msg, resp.Result, string(body)),
Body: body,
}
}
func (c *Client) endpoint(path string, query url.Values) (string, string) {
base := strings.TrimRight(c.cfg.BaseURL, "/")
path = strings.TrimLeft(path, "/")
u := fmt.Sprintf("%s/api/endpoint/%s/%s/%s", base, url.PathEscape(c.cfg.MerchantCode), url.PathEscape(c.tenantCode()), path)
if len(query) == 0 {
return u, ""
}
queryString := query.Encode()
return u + "?" + queryString, queryString
}
func (c *Client) baseHeaders(queryString string, body []byte) map[string]string {
headers := map[string]string{
"os": c.os(),
"osType": c.osType(),
"businessType": c.businessType(),
"language": c.language(),
}
if !c.cfg.EnableSign || c.cfg.SignKey == "" {
return headers
}
timestamps := strconv.FormatInt(time.Now().UnixMilli(), 10)
headers["timestamps"] = timestamps
signTimestamp := timestamps
if c.cfg.EncryptTimestamp && c.cfg.AESKey != "" {
if encrypted := encryptTimestamp(timestamps, c.cfg.AESKey); encrypted != "" {
headers["envTimestamps"] = encrypted
}
}
signBody := queryString
if len(body) > 0 {
signBody += string(body)
}
sum := md5.Sum([]byte(signBody + signTimestamp + c.cfg.SignKey))
headers["sign"] = hex.EncodeToString(sum[:])
return headers
}
func (c *Client) os() string {
if c.cfg.OS != "" {
return c.cfg.OS
}
return "web"
}
func (c *Client) osType() string {
if c.cfg.OSType != "" {
return c.cfg.OSType
}
return "web"
}
func (c *Client) businessType() string {
if c.cfg.BusinessType != "" {
return c.cfg.BusinessType
}
return "im_sdk_customer"
}
func (c *Client) language() string {
if c.cfg.Language != "" {
return c.cfg.Language
}
return "zh-cn"
}
func (c *Client) tokenTTL() int64 {
if c.cfg.TokenTTL > 0 {
return c.cfg.TokenTTL
}
return 86400
}
func (c *Client) appKey() string {
if c.cfg.AppKey != "" {
return c.cfg.AppKey
}
if c.cfg.MerchantCode == "" || c.cfg.TenantCode == "" {
return ""
}
return c.cfg.MerchantCode + "#" + c.cfg.TenantCode
}
func (c *Client) tenantCode() string {
if c.cfg.TenantCode != "" {
return c.cfg.TenantCode
}
return c.cfg.AppKey
}
func encryptTimestamp(timestamps, key string) string {
block, err := aes.NewCipher([]byte(key))
if err != nil {
return ""
}
src := zeroPadding([]byte(timestamps), block.BlockSize())
dst := make([]byte, len(src))
ecb.NewECBEncrypter(block).CryptBlocks(dst, src)
return base64.StdEncoding.EncodeToString(dst)
}
func zeroPadding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
if padding == blockSize {
padding = blockSize
}
return append(src, bytes.Repeat([]byte{0}, padding)...)
}