package GoUtil import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/md5" crand "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/gob" "encoding/hex" "fmt" "io" "math/rand" "net/http" "reflect" "strconv" "strings" "sync" "time" ) var bufferPool = sync.Pool{ New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, 128*1024)) }, } // 实例化一个通过字符串映射函数切片的map var eventByName = make(map[string][]*EventObj) type EventObj struct { Callback func([]interface{}) Obj interface{} } const ( SECRET_KEY = ")VQbB(vpy=U(wcp)" CONDITION_EQ = "=" // 等于 CONDITION_GT = ">" // 大于 CONDITION_LT = "<" // 小于 CONDITION_GE = ">=" // 大于等于 CONDITION_LE = "<=" // 小于等于 LETTER = "GhCvgqSNTUMVeRfwakiYmcxWKtJQpZrDIBXnPyLsAFdzjHbulE" ) // 加密字符串 func Encrypt(plainText string) (string, error) { block, err := aes.NewCipher([]byte(SECRET_KEY)) if err != nil { return "", err } cipherText := make([]byte, aes.BlockSize+len(plainText)) iv := cipherText[:aes.BlockSize] if _, err := io.ReadFull(crand.Reader, iv); err != nil { return "", err } stream := cipher.NewCFBEncrypter(block, iv) stream.XORKeyStream(cipherText[aes.BlockSize:], []byte(plainText)) return base64.URLEncoding.EncodeToString(cipherText), nil } // 解密字符串 func Decrypt(cipherText string) (string, error) { cipherTextBytes, err := base64.URLEncoding.DecodeString(cipherText) if err != nil { return "", err } block, err := aes.NewCipher([]byte(SECRET_KEY)) if err != nil { return "", err } if len(cipherTextBytes) < aes.BlockSize { return "", fmt.Errorf("cipherText too short") } iv := cipherTextBytes[:aes.BlockSize] cipherTextBytes = cipherTextBytes[aes.BlockSize:] stream := cipher.NewCFBDecrypter(block, iv) stream.XORKeyStream(cipherTextBytes, cipherTextBytes) return string(cipherTextBytes), nil } func (o *EventObj) isEqual(tar *EventObj) bool { if reflect.ValueOf(o.Callback).Pointer() == reflect.ValueOf(tar.Callback).Pointer() && o.Obj == tar.Obj { return true } return false } // 注册事件,提供事件名和回调函数 func RegisterEvent(name string, callback func([]interface{}), Obj interface{}) { eo := new(EventObj) eo.Callback = callback eo.Obj = Obj // 通过名字查找事件列表 list := eventByName[name] // 在列表切片中添加函数 list = append(list, eo) // 将修改的事件列表切片保存回去 eventByName[name] = list } // 调用事件 func CallEvent(name string, param []interface{}) { // 通过名字找到事件列表 list := eventByName[name] // 遍历这个事件的所有回调 for _, eo := range list { // 传入参数调用回调 eo.Callback(param) } } func RemoveEvent(name string, callback func([]interface{}), Obj interface{}) { // 通过名字找到事件列表 list := eventByName[name] j := 0 eo := new(EventObj) eo.Callback = callback eo.Obj = Obj // 遍历这个事件的所有回调 for _, v := range list { if !v.isEqual(eo) { list[j] = v j++ } } eventByName[name] = list[:j] } func DeleteEleByValue(list []int, ele int) []int { j := 0 for _, v := range list { if v != ele { list[j] = v j++ } } ret := list[:j] return ret } func BoolToInt32(b bool) int32 { if b { return 1 } return 0 } func IfTrue(a bool, b, c interface{}) interface{} { if a { return b } return c } func Int64(a interface{}) int64 { if a == nil { return 0 } switch v := a.(type) { case int: return int64(v) case int32: return int64(v) case int64: return v case float64: return int64(v) } return 0 } func Int(a interface{}) int { if a == nil { return 0 } switch v := a.(type) { case int: return v case int32: return int(v) case int64: return int(v) case float64: return int(v) case string: r, err := strconv.Atoi(v) if err != nil { return 0 } return r } return 0 } func String(a interface{}) string { if a == nil { return "" } switch v := a.(type) { case int: return strconv.Itoa(v) case int32: return strconv.Itoa(int(v)) case int64: return strconv.Itoa(int(v)) case float64: return strconv.FormatFloat(v, 'f', -1, 64) case string: return v } return "" } func GobMarshal(data interface{}) ([]byte, error) { buf := bufferPool.Get().(*bytes.Buffer) buf.Reset() // 清空之前的数据 encode := gob.NewEncoder(buf) err := encode.Encode(data) if err != nil { bufferPool.Put(buf) // 即使出错也要归还 return nil, err } bufferPool.Put(buf) // 归还到对象池 return buf.Bytes(), nil } func GobUnmarshal(data []byte, obj interface{}) error { decode := gob.NewDecoder(bytes.NewReader(data)) err := decode.Decode(obj) if err != nil { return err } return nil } func GetServerIdByUid(uid int) int { if uid <= 100000 { return uid } if uid <= 100000000 { return uid / 100000 } return int((uid % 100000000) / 100000) } func CreateOrderSn(uid int) string { Now := time.Now() return "order_" + strconv.Itoa(uid) + "_" + Now.Format("20060102150405") + RandString(6) } // 生成指定长度的随机字符串 func RandString(n int) string { const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" r := rand.New(rand.NewSource(time.Now().UnixNano())) b := make([]byte, n) for i := range b { b[i] = letters[r.Intn(len(letters))] } return string(b) } // 生成索要卡牌唯一id func CreateCardId(From, To, CardId int) string { return fmt.Sprintf("%d_%d_%d_%d_%s", From, To, CardId, Now(), RandString(3)) } func SplitInt(str, sep string) []int { var ret []int for _, v := range strings.Split(str, sep) { ret = append(ret, Int(v)) } return ret } func Compare(x int, condition string, y int) bool { switch condition { case CONDITION_EQ: return x == y case CONDITION_GT: return x > y case CONDITION_LT: return x < y case CONDITION_GE: return x >= y case CONDITION_LE: return x <= y default: return false } } func UniKey(seed string) string { hash := md5.New() timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) io.WriteString(hash, seed+timestamp) return fmt.Sprintf("%x", hash.Sum(nil)) } func GetCatnipLockKey(Uid, GameId int) string { return fmt.Sprintf("catnip_lock_%d_%d", Uid, GameId) } func Rand6DigitNumber() string { n := rand.Intn(1000000) return fmt.Sprintf("%06d", n) } func Rand8DigitNumber() string { n := rand.Intn(100000000) return fmt.Sprintf("%08d", n) } func UniqueInts(input []int) []int { seen := make(map[int]struct{}) result := make([]int, 0, len(input)) for _, v := range input { if _, ok := seen[v]; !ok { seen[v] = struct{}{} result = append(result, v) } } return result } /* #codebase: GoUtil 修改函数UniqueStringFromInt 功能说明: 将整数转换为包含字母和数字的唯一字符串 算法步骤: 1. 将输入整数格式化为5位数字符串(不足前补0) 2. 分割字符串:前2位、中2位、最后1位 3. 将各段转为整数,作为字母表索引 4. 从预定义字母表中选择对应字符 5. 随机生成2个数字字符 6. 随机插入数字字符到字母串中 7. 返回最终唯一标识字符串 */ func UniqueStringFromInt(n int) string { n = n % 100000 // 确保n在0-99999范围内 str := fmt.Sprintf("%05d", n) a1 := str[0:2] a2 := str[2:4] a3 := str[4:5] s1, _ := strconv.Atoi(a1) s2, _ := strconv.Atoi(a2) s3, _ := strconv.Atoi(a3) // 修改索引计算逻辑 var letter1, letter2 string if s1 >= len(LETTER) { letter1 = string(LETTER[s1%len(LETTER)]) + strconv.Itoa(s1/len(LETTER)) } else { letter1 = string(LETTER[s1]) } if s2 >= len(LETTER) { idx := (s2) % len(LETTER) letter2 = string(LETTER[idx]) + strconv.Itoa(s2/len(LETTER)) } else { letter2 = string(LETTER[s2]) } letter3 := string(LETTER[s3+20]) letter := fmt.Sprintf("%s%s%s", letter1, letter2, letter3) lastLetter := 5 - len(letter) indices := rand.Perm(8)[:2] chars := []byte{byte('2' + indices[0]), byte('2' + indices[1])} if lastLetter > 0 { for i := 0; i < lastLetter; i++ { insertPos1 := rand.Intn(len(letter) + 1) letter = letter[:insertPos1] + string(chars[0]) + letter[insertPos1:] } } pos := rand.Intn(len(LETTER)) letter += string(LETTER[pos]) return fmt.Sprintf("%s-%s", letter[:3], letter[3:]) } // 反解析 UniqueStringFromInt 生成的字符串,返回原始整数(0-99999),失败返回 -1 func ParseUniqueStringToInt(s string) int { arr := strings.Split(s, "-") // 去除字符串中大于1的数字 s1 := arr[2] + arr[3] s1 = s1[:len(s1)-1] // 去掉最后一个字符 s2 := "" for _, ch := range s1 { if ch < '2' || ch > '9' { s2 += string(ch) } } index1 := 0 s3 := "" for i := 0; i < len(s2); i++ { index := strings.Index(LETTER, string(s2[i])) if i < len(s2)-1 && s2[i+1] == '1' { index += len(LETTER) i++ } index1++ if i == len(s2)-1 { index -= 20 // 最后一位是字母,减去20 s3 += fmt.Sprintf("%d", index) } else { s3 += fmt.Sprintf("%02d", index) } } return Int(s3) } func GenerateShuffledAlphabet() string { // 包含所有大小写字母的字符串 alphabet := "abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ" // 转换为字节切片以便打乱 bytes := []byte(alphabet) // 使用当前时间作为随机种子 rand.Seed(time.Now().UnixNano()) // Fisher-Yates 洗牌算法 for i := len(bytes) - 1; i > 0; i-- { j := rand.Intn(i + 1) bytes[i], bytes[j] = bytes[j], bytes[i] } return string(bytes) } // 根据IP获取国家名称 func GetCountryByIP(ip string) (string, error) { return "", nil // resp, err := http.Get("https://ipapi.co/" + ip + "/country_name/") // if err != nil { // return "", err // } // defer resp.Body.Close() // buf := new(bytes.Buffer) // buf.ReadFrom(resp.Body) // return strings.TrimSpace(buf.String()), nil } // 根据国家名称获取ISO 3166-1国家码 func GetISOCodeByCountry(country string) (string, error) { // 简单映射,可以扩展为更完整的映射表 countryMap := map[string]string{ "Afghanistan": "004", // 阿富汗 "Albania": "008", // 阿尔巴尼亚 "Algeria": "012", // 阿尔及利亚 "Angola": "024", // 安哥拉 "Argentina": "032", // 阿根廷 "Austria": "040", // 奥地利 "Azerbaijan": "031", // 阿塞拜疆 "Bahrain": "048", // 巴林 "Bangladesh": "050", // 孟加拉国 "Belgium": "056", // 比利时 "Bolivia": "068", // 玻利维亚 "Bosnia and Herzegovina": "070", // 波斯尼亚和黑塞哥维那 "Brazil": "076", // 巴西 "Bulgaria": "100", // 保加利亚 "Canada": "124", // 加拿大 "Chile": "152", // 智利 "China": "156", // 中国 "Colombia": "170", // 哥伦比亚 "Costa Rica": "188", // 哥斯达黎加 "Croatia": "191", // 克罗地亚 "Cuba": "192", // 古巴 "Denmark": "208", // 丹麦 "Ecuador": "218", // 厄瓜多尔 "Egypt": "818", // 埃及 "Ethiopia": "231", // 埃塞俄比亚 "Finland": "246", // 芬兰 "France": "250", // 法国 "Germany": "276", // 德国 "Ghana": "288", // 加纳 "Greece": "300", // 希腊 "Hungary": "348", // 匈牙利 "India": "356", // 印度 "Indonesia": "360", // 印度尼西亚 "Iran": "364", // 伊朗 "Iraq": "368", // 伊拉克 "Italy": "380", // 意大利 "Japan": "392", // 日本 "Jordan": "400", // 约旦 "Kenya": "404", // 肯尼亚 "Kuwait": "414", // 科威特 "Lebanon": "422", // 黎巴嫩 "Malaysia": "458", // 马来西亚 "Mexico": "484", // 墨西哥 "Morocco": "504", // 摩洛哥 "Nigeria": "566", // 尼日利亚 "Norway": "578", // 挪威 "Pakistan": "586", // 巴基斯坦 "Peru": "604", // 秘鲁 "Philippines": "608", // 菲律宾 "Poland": "616", // 波兰 "Portugal": "620", // 葡萄牙 "Qatar": "634", // 卡塔尔 "Romania": "642", // 罗马尼亚 "Russia": "643", // 俄罗斯 "Saudi Arabia": "682", // 沙特阿拉伯 "South Africa": "710", // 南非 "South Korea": "410", // 韩国 "Spain": "724", // 西班牙 "Sweden": "752", // 瑞典 "Switzerland": "756", // 瑞士 "Thailand": "764", // 泰国 "Turkey": "792", // 土耳其 "Ukraine": "804", // 乌克兰 "United Kingdom": "826", // 英国 "United States": "840", // 美国 "Vietnam": "704", // 越南 "Zimbabwe": "716", // 津巴布韦 } if code, ok := countryMap[country]; ok { return code, nil } return "", fmt.Errorf("country code not found for %s", country) } // 综合函数:根据IP获取ISO 3166-1国家码 func GetISOCodeByIP(ip string) (string, error) { country, err := GetCountryByIP(ip) if err != nil { return "", err } return GetISOCodeByCountry(country) } func GetVarKey(Uid int) string { return fmt.Sprintf("var_%d", Uid) } const ( PROJECT_ID = "20659" ACCESS_ID = "a3d8c1f0e5b72a49" NOTIFICATION_SECRET_KEY = "K8pYrR6fXcVzWbAqN3mHsD4gJtL9iUvO1" ) func NotifyPlayer(uid, pushid int, title, content string) { url := "https://tygapi-new.tuyooglobal.com/api/push/ga/push_message/project_trigger" method := "POST" executeId := fmt.Sprintf("%s_%s", PROJECT_ID, Rand8DigitNumber()+Rand8DigitNumber()) payload := strings.NewReader(`[ { "projectId": "` + PROJECT_ID + `", "platform": "auto", "pushId": "` + PROJECT_ID + `_` + fmt.Sprintf("%08d", pushid) + `", "executeId": "` + executeId + `", "environment": "production", "title": "` + title + `", "body": "` + content + `", "userId": [ "` + strconv.Itoa(uid) + `" ] } ]`) client := &http.Client{} req, err := http.NewRequest(method, url, payload) if err != nil { fmt.Println(err) return } d, _ := time.ParseDuration("30m") timestamp := time.Now().Add(d).Unix() var strBuilder strings.Builder strBuilder.WriteString("Access-Id=" + ACCESS_ID + "&Random=a2dS8Iyak03Ma91JC1xR&Secret-Type=forever&Sign-Type=sha256&Timestamp=") strBuilder.WriteString(strconv.Itoa(int(timestamp))) strBuilder.WriteString(":" + NOTIFICATION_SECRET_KEY) m := sha256.New() m.Write([]byte(strBuilder.String())) signature := hex.EncodeToString(m.Sum(nil)) req.Header.Add("Access-Id", ACCESS_ID) req.Header.Add("Secret-Type", "forever") req.Header.Add("Sign-Type", "sha256") req.Header.Add("Random", "a2dS8Iyak03Ma91JC1xR") req.Header.Add("Timestamp", strconv.Itoa(int(timestamp))) req.Header.Add("Signature", signature) req.Header.Add("Content-Type", "application/json") fmt.Print(req) res, err := client.Do(req) if err != nil { fmt.Println(err) return } defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { fmt.Println(err) return } fmt.Println(string(body)) }