Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Support unit test in go.1 #175

Merged
merged 1 commit into from
Sep 7, 2023
Merged

Conversation

a67793581
Copy link
Contributor

@a67793581 a67793581 commented Sep 7, 2023

🔍 What type of PR is this?

Enhance system.

Add one of the following kinds:
/kind feature

Optionally add one or more of the following kinds if applicable:
/kind failing-test

👀 What this PR does / why we need it:

  • My pull request adheres to the code style of this project
  • My code requires changes to the documentation
  • I have updated the documentation as required
  • All the tests have passed

🅰 Which issue(s) this PR fixes:

Fix #117

📝 Special notes for your reviewer:

🎯 Describe how to verify it

https://github.com/a67793581/OpenKF/tree/feat/test/server/test

📑 Additional documentation e.g., RFC, notion, Google docs, usage docs, etc.:

https://github.com/a67793581/OpenKF/tree/feat/test/server/test

@pull-request-size pull-request-size bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Sep 7, 2023
@a67793581 a67793581 temporarily deployed to openkf September 7, 2023 10:40 — with GitHub Actions Inactive
@a67793581 a67793581 temporarily deployed to openkf September 7, 2023 10:40 — with GitHub Actions Inactive
@kubbot
Copy link
Contributor

kubbot commented Sep 7, 2023

CLA Assistant Lite bot 🤖 All Contributors have signed the openkf CLA.
The signed information is recorded 🤖here

@a67793581
Copy link
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@a67793581
Copy link
Contributor Author

recheck

openimbot added a commit to openim-sigs/cla that referenced this pull request Sep 7, 2023
@a67793581
Copy link
Contributor Author

image

@a67793581
Copy link
Contributor Author

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>cache: Go Coverage Report</title>
		<style>
			body {
				background: black;
				color: rgb(80, 80, 80);
			}
			body, pre, #legend span {
				font-family: Menlo, monospace;
				font-weight: bold;
			}
			#topbar {
				background: black;
				position: fixed;
				top: 0; left: 0; right: 0;
				height: 42px;
				border-bottom: 1px solid rgb(80, 80, 80);
			}
			#content {
				margin-top: 50px;
			}
			#nav, #legend {
				float: left;
				margin-left: 10px;
			}
			#legend {
				margin-top: 12px;
			}
			#nav {
				margin-top: 10px;
			}
			#legend span {
				margin: 0 5px;
			}
			.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }

		</style>
	</head>
	<body>
		<div id="topbar">
			<div id="nav">
				<select id="files">
				
				<option value="file0">github.com/OpenIMSDK/OpenKF/server/internal/dal/cache/redis.go (0.0%)</option>
				
				<option value="file1">github.com/OpenIMSDK/OpenKF/server/internal/middleware/hooks/url_trie/hook.go (5.9%)</option>
				
				<option value="file2">github.com/OpenIMSDK/OpenKF/server/internal/middleware/hooks/url_trie/trie.go (100.0%)</option>
				
				<option value="file3">github.com/OpenIMSDK/OpenKF/server/internal/service/bot.go (0.0%)</option>
				
				<option value="file4">github.com/OpenIMSDK/OpenKF/server/internal/service/common.go (0.0%)</option>
				
				<option value="file5">github.com/OpenIMSDK/OpenKF/server/internal/service/community.go (0.0%)</option>
				
				<option value="file6">github.com/OpenIMSDK/OpenKF/server/internal/service/mail.go (0.0%)</option>
				
				<option value="file7">github.com/OpenIMSDK/OpenKF/server/internal/service/service.go (0.0%)</option>
				
				<option value="file8">github.com/OpenIMSDK/OpenKF/server/internal/service/slack.go (0.0%)</option>
				
				<option value="file9">github.com/OpenIMSDK/OpenKF/server/internal/service/user.go (0.0%)</option>
				
				<option value="file10">github.com/OpenIMSDK/OpenKF/server/internal/service/user_dispatch.go (20.8%)</option>
				
				<option value="file11">github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/auth/auth.go (92.9%)</option>
				
				<option value="file12">github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/msg/msg.go (76.9%)</option>
				
				<option value="file13">github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/user/user.go (90.0%)</option>
				
				<option value="file14">github.com/OpenIMSDK/OpenKF/server/pkg/utils/password.go (100.0%)</option>
				
				<option value="file15">github.com/OpenIMSDK/OpenKF/server/pkg/utils/strings.go (100.0%)</option>
				
				<option value="file16">github.com/OpenIMSDK/OpenKF/server/pkg/utils/uuid.go (100.0%)</option>
				
				<option value="file17">github.com/OpenIMSDK/OpenKF/server/pkg/utils/valid.go (100.0%)</option>
				
				</select>
			</div>
			<div id="legend">
				<span>not tracked</span>
			
				<span class="cov0">not covered</span>
				<span class="cov8">covered</span>
			
			</div>
		</div>
		<div id="content">
		
		<pre class="file" id="file0" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cache

import (
        "context"
        "time"

        "github.com/go-redis/redis/v8"
        "github.com/pkg/errors"
)

var _ Cache = new(cache)

// Cache cache interface.
type Cache interface {
        // default
        Set(ctx context.Context, key, value string, ttl time.Duration) error
        Get(ctx context.Context, key string) (string, error)
        TTL(ctx context.Context, key string) (time.Duration, error)
        Expire(ctx context.Context, key string, ttl time.Duration) bool
        ExpireAt(ctx context.Context, key string, ttl time.Time) bool
        Del(ctx context.Context, key string) bool
        Exists(ctx context.Context, keys ...string) bool
        Incr(ctx context.Context, key string) int64
        Close() error

        // List
        LPush(ctx context.Context, key string, values ...interface{}) error
        RPush(ctx context.Context, key string, values ...interface{}) error
        LPop(ctx context.Context, key string) (string, error)
        RPop(ctx context.Context, key string) (string, error)
        LRange(ctx context.Context, key string, start, stop int64) ([]string, error)
        LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error)
        LTrim(ctx context.Context, key string, start, stop int64) error
        LLen(ctx context.Context, key string) (int64, error)

        // ZSet
        ZAdd(ctx context.Context, key string, members ...*redis.Z) (int64, error)
        ZRem(ctx context.Context, key string, members ...interface{}) (int64, error)
        ZRange(ctx context.Context, key string, start, stop int64) ([]string, error)

        // Map
        HSet(ctx context.Context, key, field string, value string) error
        HGet(ctx context.Context, key, field string) (string, error)
        HGetAll(ctx context.Context, key string) (map[string]string, error)
        HDel(ctx context.Context, key string, fields ...string) (int64, error)
        HExists(ctx context.Context, key, field string) (bool, error)
        HIncrBy(ctx context.Context, key, field string, incr int64) (int64, error)
        HKeys(ctx context.Context, key string) ([]string, error)
        HLen(ctx context.Context, key string) (int64, error)
        HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error)
        HMSet(ctx context.Context, key string, values ...interface{}) error

        // tx
        Pipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error)
        Pipline() redis.Pipeliner
        TxPipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error)
        TxPipeline() redis.Pipeliner

        Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error
}

// cache cache dao representative.
type cache struct {
        client *redis.Client
}

// Use return cache db.
func Use(client *redis.Client) Cache <span class="cov0" title="0">{
        return &amp;cache{
                client: client,
        }
}</span>

// Get get value.
func (c *cache) Get(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
        value, err := c.client.Get(ctx, key).Result()
        if err != nil </span><span class="cov0" title="0">{
                return "", errors.Wrapf(err, "redis get key: %s err", key)
        }</span>

        <span class="cov0" title="0">return value, nil</span>
}

// Set set key value pair to redis.
func (c *cache) Set(ctx context.Context, key string, value string, ttl time.Duration) error <span class="cov0" title="0">{
        if err := c.client.Set(ctx, key, value, ttl).Err(); err != nil </span><span class="cov0" title="0">{
                return errors.Wrapf(err, "redis set key: %s err", key)
        }</span>

        <span class="cov0" title="0">return nil</span>
}

// TTL get ttl of key.
func (c *cache) TTL(ctx context.Context, key string) (time.Duration, error) <span class="cov0" title="0">{
        ttl, err := c.client.TTL(ctx, key).Result()
        if err != nil </span><span class="cov0" title="0">{
                return -1, errors.Wrapf(err, "redis get key: %s err", key)
        }</span>

        <span class="cov0" title="0">return ttl, nil</span>
}

// Expire expire key.
func (c *cache) Expire(ctx context.Context, key string, ttl time.Duration) bool <span class="cov0" title="0">{
        ok, _ := c.client.Expire(ctx, key, ttl).Result()

        return ok
}</span>

// ExpireAt expire key at time.
func (c *cache) ExpireAt(ctx context.Context, key string, ttl time.Time) bool <span class="cov0" title="0">{
        ok, _ := c.client.ExpireAt(ctx, key, ttl).Result()

        return ok
}</span>

// Exists check some keys are exist.
func (c *cache) Exists(ctx context.Context, keys ...string) bool <span class="cov0" title="0">{
        if len(keys) == 0 </span><span class="cov0" title="0">{
                return true
        }</span>
        <span class="cov0" title="0">value, _ := c.client.Exists(ctx, keys...).Result()

        return value &gt; 0</span>
}

// Del delete key.
func (c *cache) Del(ctx context.Context, key string) bool <span class="cov0" title="0">{
        if key == "" </span><span class="cov0" title="0">{
                return true
        }</span>

        <span class="cov0" title="0">value, _ := c.client.Del(ctx, key).Result()

        return value &gt; 0</span>
}

// Incr incr key.
func (c *cache) Incr(ctx context.Context, key string) int64 <span class="cov0" title="0">{
        value, _ := c.client.Incr(ctx, key).Result()

        return value
}</span>

// Close close redis client.
func (c *cache) Close() error <span class="cov0" title="0">{
        return c.client.Close()
}</span>

// LPush left push value to list.
func (c *cache) LPush(ctx context.Context, key string, values ...interface{}) error <span class="cov0" title="0">{
        return c.client.LPush(ctx, key, values...).Err()
}</span>

// RPush right push value to list.
func (c *cache) RPush(ctx context.Context, key string, values ...interface{}) error <span class="cov0" title="0">{
        return c.client.RPush(ctx, key, values...).Err()
}</span>

// LPop left pop value from list.
func (c *cache) LPop(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
        return c.client.LPop(ctx, key).Result()
}</span>

// RPop right pop value from list.
func (c *cache) RPop(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
        return c.client.RPop(ctx, key).Result()
}</span>

// LRange get list value from start to stop.
func (c *cache) LRange(ctx context.Context, key string, start, stop int64) ([]string, error) <span class="cov0" title="0">{
        return c.client.LRange(ctx, key, start, stop).Result()
}</span>

// LRem remove count value from list.
func (c *cache) LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error) <span class="cov0" title="0">{
        return c.client.LRem(ctx, key, count, value).Result()
}</span>

// LTrim trim list from start to stop.
func (c *cache) LTrim(ctx context.Context, key string, start, stop int64) error <span class="cov0" title="0">{
        return c.client.LTrim(ctx, key, start, stop).Err()
}</span>

// LLen get list length.
func (c *cache) LLen(ctx context.Context, key string) (int64, error) <span class="cov0" title="0">{
        return c.client.LLen(ctx, key).Result()
}</span>

// Pipelined pipelined.
func (c *cache) Pipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) <span class="cov0" title="0">{
        return c.client.Pipelined(ctx, fn)
}</span>

// Pipline pipline.
func (c *cache) Pipline() redis.Pipeliner <span class="cov0" title="0">{
        return c.client.Pipeline()
}</span>

// TxPipelined tx pipelined.
func (c *cache) TxPipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) <span class="cov0" title="0">{
        return c.client.TxPipelined(ctx, fn)
}</span>

// TxPipeline tx pipeline.
func (c *cache) TxPipeline() redis.Pipeliner <span class="cov0" title="0">{
        return c.client.TxPipeline()
}</span>

// Watch watch.
func (c *cache) Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error <span class="cov0" title="0">{
        return c.client.Watch(ctx, fn, keys...)
}</span>

// LPopAndRPush lpop and rpush.
func (c *cache) LPopAndRPush(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
        tx := c.client.TxPipeline()
        defer tx.Close()

        // check length
        length, err := c.client.LLen(ctx, key).Result()
        if err != nil || length &lt;= 1 </span><span class="cov0" title="0">{
                return "", errors.Wrapf(err, "list is not exists or length less than 1: %s err", key)
        }</span>

        // get head value
        <span class="cov0" title="0">headVal, _ := c.client.LIndex(ctx, key, 0).Result()

        _ = tx.Process(ctx, c.client.LPop(ctx, key))
        _ = tx.Process(ctx, c.client.RPush(ctx, key, headVal))

        _, err = tx.Exec(ctx)
        if err != nil </span><span class="cov0" title="0">{
                return "", err
        }</span>

        <span class="cov0" title="0">return headVal, nil</span>
}

// ZAdd zset add.
func (c *cache) ZAdd(ctx context.Context, key string, members ...*redis.Z) (int64, error) <span class="cov0" title="0">{
        return c.client.ZAdd(ctx, key, members...).Result()
}</span>

// ZRem zset remove.
func (c *cache) ZRem(ctx context.Context, key string, members ...interface{}) (int64, error) <span class="cov0" title="0">{
        return c.client.ZRem(ctx, key, members...).Result()
}</span>

// ZRange zset range.
func (c *cache) ZRange(ctx context.Context, key string, start, stop int64) ([]string, error) <span class="cov0" title="0">{
        return c.client.ZRange(ctx, key, start, stop).Result()
}</span>

// // EnqueueStringLPush enqueue key and push left.
// func (c *cache) EnqueueStringLPush(ctx context.Context, key, value string) error {
//         c.mu.Lock()
//         defer c.mu.Unlock()

//         err := c.client.LPush(ctx, key, value).Err()
//         if err != nil {
//                 return errors.Wrapf(err, "redis enqueue key: %s , value: %s, err: %s", key, value, err.Error())
//         }

//         return nil
// }

// // DequeueStringRPop dequeue key and pop right.
// func (c *cache) DequeueStringRPop(ctx context.Context, key string) (string, error) {
//         c.mu.Lock()
//         defer c.mu.Unlock()

//         value, err := c.client.RPop(ctx, key).Result()
//         if err != nil {
//                 return "", errors.Wrapf(err, "redis dequeue key: %s , err: %s", key, err.Error())
//         }

//         return value, nil
// }

// func (c *cache) TxPipeline(ctx context.Context) redis.Pipeliner {
//         return c.client.TxPipeline()
// }

// HSet hset.
func (c *cache) HSet(ctx context.Context, key, field string, value string) error <span class="cov0" title="0">{
        return c.client.HSet(ctx, key, field, value).Err()
}</span>

// HGet hget.
func (c *cache) HGet(ctx context.Context, key, field string) (string, error) <span class="cov0" title="0">{
        return c.client.HGet(ctx, key, field).Result()
}</span>

// HGetAll hgetall.
func (c *cache) HGetAll(ctx context.Context, key string) (map[string]string, error) <span class="cov0" title="0">{
        return c.client.HGetAll(ctx, key).Result()
}</span>

// HDel hdel.
func (c *cache) HDel(ctx context.Context, key string, fields ...string) (int64, error) <span class="cov0" title="0">{
        return c.client.HDel(ctx, key, fields...).Result()
}</span>

// HExists hexists.
func (c *cache) HExists(ctx context.Context, key, field string) (bool, error) <span class="cov0" title="0">{
        return c.client.HExists(ctx, key, field).Result()
}</span>

// HIncrBy hincrby.
func (c *cache) HIncrBy(ctx context.Context, key, field string, incr int64) (int64, error) <span class="cov0" title="0">{
        return c.client.HIncrBy(ctx, key, field, incr).Result()
}</span>

// HKeys hkeys.
func (c *cache) HKeys(ctx context.Context, key string) ([]string, error) <span class="cov0" title="0">{
        return c.client.HKeys(ctx, key).Result()
}</span>

// HLen hlen.
func (c *cache) HLen(ctx context.Context, key string) (int64, error) <span class="cov0" title="0">{
        return c.client.HLen(ctx, key).Result()
}</span>

// HMGet hmget.
func (c *cache) HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error) <span class="cov0" title="0">{
        return c.client.HMGet(ctx, key, fields...).Result()
}</span>

// HMSet hmset.
func (c *cache) HMSet(ctx context.Context, key string, values ...interface{}) error <span class="cov0" title="0">{
        return c.client.HMSet(ctx, key, values).Err()
}</span>
</pre>
		
		<pre class="file" id="file1" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package urltrie

import (
        "net/url"

        "github.com/gin-gonic/gin"

        "github.com/OpenIMSDK/OpenKF/server/pkg/log"
)

// Mapping for store url pattern and hook.
var hookTrie *Trie

func init() <span class="cov8" title="1">{
        hookTrie = NewTrie()
}</span>

// RegisterHook register url &amp; hook to trie.
func RegisterHook(hook Hook) <span class="cov0" title="0">{
        hookTrie.InsertBatch(hook.Patterns(), hook)
}</span>

// RunHook enable hook for interceptor.
func RunHook() gin.HandlerFunc <span class="cov0" title="0">{
        return func(c *gin.Context) </span><span class="cov0" title="0">{
                raw := c.Request.URL.Path

                // Get path from url
                p, err := url.Parse(raw)
                if err != nil </span><span class="cov0" title="0">{
                        log.Errorf("Hook", "parse url error: %v", err)
                }</span>

                <span class="cov0" title="0">path := p.Path
                hooks, ok := hookTrie.Match(path)
                if !ok </span><span class="cov0" title="0">{
                        c.Next()

                        return
                }</span>

                // Run all before hooks
                <span class="cov0" title="0">for _, hook := range hooks </span><span class="cov0" title="0">{
                        hook.BeforeRun(c)
                }</span>

                // Run controllers
                <span class="cov0" title="0">c.Next()

                // Run all after hooks
                for _, hook := range hooks </span><span class="cov0" title="0">{
                        hook.AfterRun(c)
                }</span>
        }
}
</pre>
		
		<pre class="file" id="file2" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package urltrie

import (
        "sort"
        "strings"

        "github.com/gin-gonic/gin"
)

// Hook for interceptor.
type Hook interface {
        // Register with url pattern
        // Support * wildcard, you can use it like this:
        // /api/v1/*, and the url like /api/v1/123 will be matched
        //
        // Now we support multi urls.
        Patterns() []string

        // Priority will set the priority of the hook
        Priority() int64

        // Hooks
        // BeforeRun will be called before controller, you can do something here
        BeforeRun(c *gin.Context)
        // AfterRun will be called after controller, you can do something here
        AfterRun(c *gin.Context)
}

type sortedHook []Hook

func (sh sortedHook) Len() int <span class="cov8" title="1">{ return len(sh) }</span>

func (sh sortedHook) Less(i, j int) bool <span class="cov8" title="1">{ return sh[i].Priority() &gt; sh[j].Priority() }</span>

func (sh sortedHook) Swap(i, j int) <span class="cov8" title="1">{ sh[i], sh[j] = sh[j], sh[i] }</span>

const _wildcard = "*"

type node struct {
        children map[string]*node
        hooks    []Hook
        data     string

        // for wildcard
        isWildcard bool
        isEnd      bool
}

// Trie is a tree for url.
type Trie struct {
        root *node
}

// NewTrie returns a new Trie.
func NewTrie() *Trie <span class="cov8" title="1">{
        return &amp;Trie{
                root: &amp;node{
                        children: make(map[string]*node),
                },
        }
}</span>

// InsertBatch insert urls with hooks.
func (t *Trie) InsertBatch(urls []string, hooks ...Hook) <span class="cov8" title="1">{
        for _, url := range urls </span><span class="cov8" title="1">{
                t.insert(url, hooks...)
        }</span>
}

// Insert insert url with hooks.
func (t *Trie) insert(url string, hooks ...Hook) <span class="cov8" title="1">{
        current := t.root

        // split url with '/'
        parts := strings.Split(url, "/")
        for _, part := range parts </span><span class="cov8" title="1">{
                if part == "" </span><span class="cov8" title="1">{
                        continue</span>
                }

                <span class="cov8" title="1">child, exists := current.children[part]
                if !exists </span><span class="cov8" title="1">{
                        child = &amp;node{
                                children: make(map[string]*node),
                                data:     part,
                        }
                        // match wildcard
                        if part == _wildcard </span><span class="cov8" title="1">{
                                child.isWildcard = true
                        }</span>

                        // set child node
                        <span class="cov8" title="1">current.children[part] = child</span>
                }

                <span class="cov8" title="1">current = child</span>
        }

        // Set hooks to last node
        <span class="cov8" title="1">current.isEnd = true
        current.hooks = append(current.hooks, hooks...)
        sortedHooks := sortedHook(current.hooks)
        sort.Sort(sortedHooks)
        current.hooks = sortedHooks</span>
}

// Match match url with hooks.
func (t *Trie) Match(url string) ([]Hook, bool) <span class="cov8" title="1">{
        current := t.root

        parts := strings.Split(url, "/")
        var matchedValues []Hook
        // use stack to save matched children nodes
        stack := make([]*node, 0)
        for _, c := range current.children </span><span class="cov8" title="1">{
                stack = append(stack, c)
        }</span>

        <span class="cov8" title="1">for _, part := range parts </span><span class="cov8" title="1">{
                if part == "" </span><span class="cov8" title="1">{
                        continue</span>
                }

                <span class="cov8" title="1">if len(stack) == 0 </span><span class="cov8" title="1">{
                        return matchedValues, len(matchedValues) &gt; 0
                }</span>

                // get current level length
                <span class="cov8" title="1">levelLen := len(stack)
                for i := 0; i &lt; levelLen; i++ </span><span class="cov8" title="1">{
                        // pop
                        current, stack = stack[0], stack[1:]
                        if current.isEnd </span><span class="cov8" title="1">{
                                if current.isWildcard || current.data == part </span><span class="cov8" title="1">{
                                        // Match the last node, append the values
                                        matchedValues = append(matchedValues, current.hooks...)
                                }</span>

                                <span class="cov8" title="1">continue</span>
                        }

                        // find wildcard node or expect node
                        <span class="cov8" title="1">if current.isWildcard || current.data == part </span><span class="cov8" title="1">{
                                for _, child := range current.children </span><span class="cov8" title="1">{
                                        stack = append(stack, child)
                                }</span>
                        }
                }
        }

        <span class="cov8" title="1">return matchedValues, len(matchedValues) &gt; 0</span>
}
</pre>
		
		<pre class="file" id="file3" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "github.com/gin-gonic/gin"

        "github.com/OpenIMSDK/OpenKF/server/internal/common"
        "github.com/OpenIMSDK/OpenKF/server/internal/config"
        "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
        systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles"
        requestparams "github.com/OpenIMSDK/OpenKF/server/internal/params/request"
        responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
        "github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)

// BotService bot service.
type BotService struct {
        Service

        SysBotDao       *dao.SysBotDao
        SysCommunityDao *dao.SysCommunityDao
}

// NewBotService return new service with gin context.
func NewBotService(c *gin.Context) *BotService <span class="cov0" title="0">{
        return &amp;BotService{
                Service: Service{
                        ctx: c,
                },
                SysBotDao:       dao.NewSysBotDao(),
                SysCommunityDao: dao.NewSysCommunityDao(),
        }
}</span>

// CreateBot create bot.
func (svc *BotService) CreateBot(cid string, params *requestparams.CreateBotParams) (string, uint, error) <span class="cov0" title="0">{
        // Create bot
        uid := utils.GenUUIDWithoutHyphen()

        communityInfo, err := svc.SysCommunityDao.FindFirstByUUID(cid)
        if err != nil </span><span class="cov0" title="0">{
                return "", 0, err
        }</span>

        <span class="cov0" title="0">bot := &amp;systemroles.SysBot{
                UUID:        uid,
                BotAddr:     params.BotAddr,
                BotPort:     params.BotPort,
                BotToken:    params.BotToken,
                Nickname:    params.Nickname,
                Avatar:      *params.Avatar,
                Description: *params.Description,
                CommunityId: communityInfo.Id,
        }
        if err = svc.SysBotDao.Create(bot); err != nil </span><span class="cov0" title="0">{
                return uid, 0, err
        }</span>

        <span class="cov0" title="0">b, _ := svc.SysBotDao.FindFirstByUUID(uid)

        // Register bot to OpenIM
        param := &amp;request.RegisterUserParams{
                Secret: config.Config.OpenIM.Secret,
                Users: []request.User{
                        {
                                UserID:   uid,
                                Nickname: params.Nickname,
                                FaceURL:  "", // Use OpenKF avatar
                        },
                },
        }
        ok, err := registerUserToOpenIM(param)
        if err != nil || !ok </span><span class="cov0" title="0">{
                // Assume that the user has been created/deleted successfully
                _ = svc.SysBotDao.Delete(b)

                return uid, b.Id, err
        }</span>

        <span class="cov0" title="0">return uid, b.Id, nil</span>
}

// GetCommunityBotList get community bot list.
func (svc *BotService) GetCommunityBotList(cid string, params *requestparams.ListPageParams) (*responseparams.ListPageResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.ListPageResponse{}
        botInfos := make([]*responseparams.BotInfoResponse, 0)

        csvc := NewCommunityService((svc.ctx).(*gin.Context))
        info, err := csvc.GetCommunityInfoByUUIDV2(cid)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Get bot list
        <span class="cov0" title="0">bots, total, err := svc.SysBotDao.FindByCommunityIdPage(info.Id, (params.Page-1)*params.PageSize, params.PageSize)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Fill response data
        <span class="cov0" title="0">for _, b := range bots </span><span class="cov0" title="0">{
                botInfos = append(botInfos, &amp;responseparams.BotInfoResponse{
                        UUID:        b.UUID,
                        BotAddr:     b.BotAddr,
                        BotPort:     b.BotPort,
                        BotToken:    b.BotToken,
                        Nickname:    b.Nickname,
                        Avatar:      b.Avatar,
                        Description: b.Description,
                })
        }</span>

        <span class="cov0" title="0">resp.List = botInfos
        resp.Page = params.Page
        resp.PageSize = params.PageSize
        resp.Total = int(total)

        return resp, nil</span>
}

// DeleteBot delete a bot.
func (svc *BotService) DeleteBot(uid string) error <span class="cov0" title="0">{
        if uid == "" </span><span class="cov0" title="0">{
                return common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">u, err := svc.SysBotDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return err
        }</span>

        <span class="cov0" title="0">err = svc.SysBotDao.Delete(u)
        if err != nil </span><span class="cov0" title="0">{
                return err
        }</span>

        <span class="cov0" title="0">return nil</span>
}

// UpdateBotInfo update bot info.
func (svc *BotService) UpdateBotInfo(params *requestparams.UpdateBotParams) (*responseparams.BotInfoResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.BotInfoResponse{}

        if params.UUID == "" </span><span class="cov0" title="0">{
                return resp, common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">b, err := svc.SysBotDao.FindFirstByUUID(params.UUID)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Update bot info
        <span class="cov0" title="0">if params.BotAddr != nil </span><span class="cov0" title="0">{
                b.BotAddr = *params.BotAddr
        }</span>
        <span class="cov0" title="0">if params.BotPort != nil </span><span class="cov0" title="0">{
                b.BotPort = *params.BotPort
        }</span>
        <span class="cov0" title="0">if params.BotToken != nil </span><span class="cov0" title="0">{
                b.BotToken = *params.BotToken
        }</span>
        <span class="cov0" title="0">if params.Nickname != nil </span><span class="cov0" title="0">{
                b.Nickname = *params.Nickname
        }</span>
        <span class="cov0" title="0">if params.Avatar != nil </span><span class="cov0" title="0">{
                b.Avatar = *params.Avatar
        }</span>
        <span class="cov0" title="0">if params.Description != nil </span><span class="cov0" title="0">{
                b.Description = *params.Description
        }</span>

        // Update user info
        <span class="cov0" title="0">if err = svc.SysBotDao.Update(b); err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Get new info
        <span class="cov0" title="0">b, _ = svc.SysBotDao.FindFirstByUUID(params.UUID)
        resp.UUID = b.UUID
        resp.BotAddr = b.BotAddr
        resp.BotPort = b.BotPort
        resp.BotToken = b.BotToken
        resp.Nickname = b.Nickname
        resp.Avatar = b.Avatar
        resp.Description = b.Description

        return resp, nil</span>
}
</pre>
		
		<pre class="file" id="file4" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "mime/multipart"

        "github.com/gin-gonic/gin"

        "github.com/OpenIMSDK/OpenKF/server/internal/conn/client"
        "github.com/OpenIMSDK/OpenKF/server/internal/utils"
)

// CommonService community service.
type CommonService struct {
        Service
}

// NewCommonService return new service with gin context.
func NewCommonService(c *gin.Context) *CommonService <span class="cov0" title="0">{
        return &amp;CommonService{
                Service: Service{
                        ctx: c,
                },
        }
}</span>

// UploadFile upload file.
func (svc *CommonService) UploadFile(file *multipart.FileHeader) (string, error) <span class="cov0" title="0">{
        fileHandler, err := file.Open()
        if err != nil </span><span class="cov0" title="0">{
                return "", err
        }</span>
        <span class="cov0" title="0">defer fileHandler.Close()

        filename := file.Filename
        objectName := utils.GenerateObjectName(filename)
        fileSize := file.Size

        err = client.PutObject(objectName, fileHandler, fileSize)
        if err != nil </span><span class="cov0" title="0">{
                return "", err
        }</span>

        <span class="cov0" title="0">url := client.GetObejctURL(objectName)

        return url, nil</span>
}
</pre>
		
		<pre class="file" id="file5" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "github.com/gin-gonic/gin"

        "github.com/OpenIMSDK/OpenKF/server/internal/common"
        "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
        systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles"
        requestparams "github.com/OpenIMSDK/OpenKF/server/internal/params/request"
        responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
        "github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)

// CommunityService community service.
type CommunityService struct {
        Service

        SysCommunityDao *dao.SysCommunityDao
}

// NewCommunityService return new service with gin context.
func NewCommunityService(c *gin.Context) *CommunityService <span class="cov0" title="0">{
        return &amp;CommunityService{
                Service: Service{
                        ctx: c,
                },
                SysCommunityDao: dao.NewSysCommunityDao(),
        }
}</span>

// Create create community.
func (svc *CommunityService) Create(community *requestparams.CommunityParams) (string, uint, error) <span class="cov0" title="0">{
        uuid := utils.GenUUIDWithoutHyphen()

        if community.Description == nil </span><span class="cov0" title="0">{
                *community.Description = ""
        }</span>
        <span class="cov0" title="0">if community.Avatar == nil </span><span class="cov0" title="0">{
                *community.Avatar = ""
        }</span>

        <span class="cov0" title="0">data := &amp;systemroles.SysCommunity{
                UUID:        uuid,
                Name:        community.Name,
                Email:       community.Email,
                Description: *community.Description,
                Avatar:      *community.Avatar,
        }

        // err := db.GetMysqlDB().Create(data).Error
        err := svc.SysCommunityDao.Create(data)
        if err != nil </span><span class="cov0" title="0">{
                return uuid, 0, err
        }</span>

        // Get community id
        <span class="cov0" title="0">c, _ := svc.SysCommunityDao.FindFirstByUUID(uuid)

        return c.UUID, c.Id, nil</span>
}

// GetCommunityInfoById get community info by id.
func (svc *CommunityService) GetCommunityInfoById(id uint) (*responseparams.CommunityInfoResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.CommunityInfoResponse{}

        if id &lt;= 0 </span><span class="cov0" title="0">{
                return resp, common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">c, err := svc.SysCommunityDao.FindFirstById(id)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        <span class="cov0" title="0">resp.UUID = c.UUID
        resp.Email = c.Email
        resp.Avatar = c.Avatar
        resp.Name = c.Name
        resp.Description = c.Description

        return resp, err</span>
}

// GetCommunityInfoByUUID get community info by uuid.
func (svc *CommunityService) GetCommunityInfoByUUID(uid string) (*responseparams.CommunityInfoResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.CommunityInfoResponse{}

        if uid == "" </span><span class="cov0" title="0">{
                return resp, common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">c, err := svc.SysCommunityDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        <span class="cov0" title="0">resp.UUID = c.UUID
        resp.Email = c.Email
        resp.Avatar = c.Avatar
        resp.Name = c.Name
        resp.Description = c.Description

        return resp, nil</span>
}

// GetCommunityInfoByUUIDV2 get community info by uuid and return SysCommunity.
func (svc *CommunityService) GetCommunityInfoByUUIDV2(uid string) (*systemroles.SysCommunity, error) <span class="cov0" title="0">{
        if uid == "" </span><span class="cov0" title="0">{
                return nil, common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">c, err := svc.SysCommunityDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return nil, err
        }</span>

        <span class="cov0" title="0">return c, nil</span>
}

// UpdateCommunity update community info.
func (svc *CommunityService) UpdateCommunity(uid string, community *requestparams.UpdateCommunityInfoParams) (*responseparams.CommunityInfoResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.CommunityInfoResponse{}

        if uid == "" </span><span class="cov0" title="0">{
                return resp, common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">info, err := svc.SysCommunityDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        <span class="cov0" title="0">if community.Name != nil </span><span class="cov0" title="0">{
                info.Name = *community.Name
        }</span>
        <span class="cov0" title="0">if community.Email != nil </span><span class="cov0" title="0">{
                info.Email = *community.Email
        }</span>
        <span class="cov0" title="0">if community.Description != nil </span><span class="cov0" title="0">{
                info.Description = *community.Description
        }</span>
        <span class="cov0" title="0">if community.Avatar != nil </span><span class="cov0" title="0">{
                info.Avatar = *community.Avatar
        }</span>

        // Update community info
        <span class="cov0" title="0">err = svc.SysCommunityDao.Update(info)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        <span class="cov0" title="0">resp.UUID = info.UUID
        resp.Email = info.Email
        resp.Avatar = info.Avatar
        resp.Name = info.Name
        resp.Description = info.Description

        return resp, nil</span>
}
</pre>
		
		<pre class="file" id="file6" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "time"

        "github.com/gin-gonic/gin"

        "github.com/OpenIMSDK/OpenKF/server/internal/common"
        "github.com/OpenIMSDK/OpenKF/server/internal/conn/client"
        "github.com/OpenIMSDK/OpenKF/server/internal/conn/db"
        "github.com/OpenIMSDK/OpenKF/server/internal/dal/cache"
        "github.com/OpenIMSDK/OpenKF/server/internal/utils"
        pkgutils "github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)

// MailService mail service.
type MailService struct {
        Service

        cache cache.Cache
}

// NewMailService return new service with gin context.
func NewMailService(c *gin.Context) *MailService <span class="cov0" title="0">{
        return &amp;MailService{
                Service: Service{
                        ctx: c,
                },
                cache: cache.Use(db.GetRedis()),
        }
}</span>

// SendCode send code to email.
func (svc *MailService) SendCode(email string) (err error) <span class="cov0" title="0">{
        if !pkgutils.IsValidEmail(email) </span><span class="cov0" title="0">{
                return common.NewError(common.I_INVALID_PARAM)
        }</span>

        // Check the code is exist.
        <span class="cov0" title="0">code, err := svc.cache.Get(svc.ctx, "code:"+email)
        // Refresh code.
        if err != nil || code == "" </span><span class="cov0" title="0">{
                code = utils.GenerateCode()
                // save code in 60s
                err = svc.cache.Set(svc.ctx, "code:"+email, code, time.Second*60)

                return err
        }</span>

        // Generate code.
        <span class="cov0" title="0">err = client.SendEmail(email, "OpenKF Admin Register", "Your verification code is "+code)

        return err</span>
}

// CheckCode check code.
func (svc *MailService) CheckCode(email, code string) bool <span class="cov0" title="0">{
        if !pkgutils.IsValidEmail(email) </span><span class="cov0" title="0">{
                return false
        }</span>

        // Check the code is exist.
        <span class="cov0" title="0">c, err := svc.cache.Get(svc.ctx, "code:"+email)
        if err != nil </span><span class="cov0" title="0">{
                return false
        }</span>

        <span class="cov0" title="0">if c == code </span><span class="cov0" title="0">{
                return true
        }</span>

        <span class="cov0" title="0">return false</span>
}
</pre>
		
		<pre class="file" id="file7" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "context"
)

// Service service.
type Service struct {
        ctx context.Context
}

// GetCtx get context.
func (svc *Service) GetCtx() context.Context <span class="cov0" title="0">{
        return svc.ctx
}</span>
</pre>
		
		<pre class="file" id="file8" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "context"
        "encoding/json"
        "fmt"
        "net"

        "github.com/gin-gonic/gin"
        "github.com/pkg/errors"
        "github.com/shomali11/slacker"
        "github.com/slack-go/slack"

        "github.com/OpenIMSDK/OpenKF/server/internal/config"
        "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
        "github.com/OpenIMSDK/OpenKF/server/internal/models/base"
        customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles"
        responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
        "github.com/OpenIMSDK/OpenKF/server/pkg/log"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/constant"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/msg"
        "github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)

// SlackService slack service.
type SlackService struct {
        Service

        CustomerSlackDao *dao.CustomerSlackDao
}

// NewSlackService return new service with Background context.
func NewSlackService(ctx context.Context) *SlackService <span class="cov0" title="0">{
        return &amp;SlackService{
                Service: Service{
                        ctx: ctx,
                },
                CustomerSlackDao: dao.NewCustomerSlackDao(),
        }
}</span>

// GetSlackConfig get slack config.
func (svc *SlackService) GetSlackConfig() (*responseparams.SlackConfigResponse, error) <span class="cov0" title="0">{
        return &amp;responseparams.SlackConfigResponse{
                BotToken:          config.Config.Slack.BotToken,
                AppToken:          config.Config.Slack.AppToken,
                AppID:             config.Config.Slack.AppID,
                ClientID:          config.Config.Slack.ClientID,
                ClientSecret:      config.Config.Slack.ClientSecret,
                SigningSecret:     config.Config.Slack.SigningSecret,
                VerificationToken: config.Config.Slack.VerificationToken,
        }, nil
}</span>

// CreateCustomer create customer if not exists.
func (svc *SlackService) CreateCustomer(userId string, profile *slack.UserProfile) (string, uint, error) <span class="cov0" title="0">{
        // check if exists
        temp, err := svc.CustomerSlackDao.FindFirstByUUID(userId)
        if temp != nil || err == nil </span><span class="cov0" title="0">{
                return temp.UUID, temp.Id, err
        }</span>

        // Set unique email
        <span class="cov0" title="0">if profile.Email == "" </span><span class="cov0" title="0">{
                profile.Email = fmt.Sprintf("%s %s", "NOEMAIL", utils.GenUUIDWithoutHyphen())
        }</span>

        <span class="cov0" title="0">customerSlack := customerroles.CustomerSlack{
                UserBase: base.UserBase{
                        // UUID: fmt.Sprintf("%s%s", dao.SLACK_PERFIX, userId),
                        UUID:        userId,
                        Email:       profile.Email,
                        Nickname:    profile.FirstName,
                        Avatar:      profile.Image512,
                        Description: profile.Title,
                        IsEnable:    true,
                },
                FirstName:             profile.FirstName,
                LastName:              profile.LastName,
                RealName:              profile.RealName,
                RealNameNormalized:    profile.RealNameNormalized,
                DisplayName:           profile.DisplayName,
                DisplayNameNormalized: profile.DisplayNameNormalized,
                Skype:                 profile.Skype,
                Phone:                 profile.Phone,
                Image24:               profile.Image24,
                Image32:               profile.Image32,
                Image48:               profile.Image48,
                Image72:               profile.Image72,
                Image192:              profile.Image192,
                Image512:              profile.Image512,
                ImageOriginal:         profile.ImageOriginal,
                Title:                 profile.Title,
                BotID:                 profile.BotID,
                ApiAppID:              profile.ApiAppID,
                StatusText:            profile.StatusText,
                StatusEmoji:           profile.StatusEmoji,
                StatusExpiration:      profile.StatusExpiration,
                Team:                  profile.Team,
        }
        if err = svc.CustomerSlackDao.Create(&amp;customerSlack); err != nil </span><span class="cov0" title="0">{
                return customerSlack.UUID, 0, err
        }</span>

        <span class="cov0" title="0">s, _ := svc.CustomerSlackDao.FindFirstByUUID(userId)

        param := &amp;request.RegisterUserParams{
                Secret: config.Config.OpenIM.Secret,
                Users: []request.User{
                        {
                                UserID:   userId,
                                Nickname: customerSlack.Nickname,
                                FaceURL:  "", // Use OpenKF avatar
                        },
                },
        }
        ok, err := registerUserToOpenIM(param)
        if err != nil || !ok </span><span class="cov0" title="0">{
                // Assume that the user has been created/deleted successfully
                _ = svc.CustomerSlackDao.Delete(s)

                return userId, s.Id, err
        }</span>

        <span class="cov0" title="0">return userId, s.Id, nil</span>
}

// GetSlackUser get slack user.
func (svc *SlackService) GetSlackUser(userId string) (*customerroles.CustomerSlack, error) <span class="cov0" title="0">{
        return svc.CustomerSlackDao.FindFirstByUUID(userId)
}</span>

// SendMsg send message to openkf.
func (svc *SlackService) SendMsg(uid, question string, botContext slacker.BotContext) error <span class="cov0" title="0">{
        udSvc := NewUserDispatchService(svc.ctx)

        // Get default staff id
        sMap := udSvc.GetSlackMap(uid)
        staffId := sMap.StaffID

        if sMap.StaffID == "" || sMap.SlackChannelID == "" </span><span class="cov0" title="0">{
                // Get staff id
                tempStaffId, err := udSvc.GetUser()
                if err != nil </span><span class="cov0" title="0">{
                        return err
                }</span>

                // Store staff, writer to cache redis map
                <span class="cov0" title="0">err = udSvc.SetSlackMap(uid, tempStaffId, botContext)
                if err != nil </span><span class="cov0" title="0">{
                        return errors.Wrapf(err, "set slack map failed")
                }</span>
                <span class="cov0" title="0">staffId = tempStaffId</span>
        }

        // Get OpenIM admin token
        <span class="cov0" title="0">uSvc := NewUserService(&amp;gin.Context{}) // TODO: Change context to same
        token, err := uSvc.GetAdminToken()
        if err != nil </span><span class="cov0" title="0">{
                return errors.Wrapf(err, "get admin token failed")
        }</span>

        // Get custom info
        <span class="cov0" title="0">customer, err := svc.CustomerSlackDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return errors.Wrapf(err, "find customer failed")
        }</span>

        <span class="cov0" title="0">msgInfo := &amp;request.MsgInfo{
                SendID:           uid,
                RecvID:           staffId,
                GroupID:          "",
                SenderNickname:   customer.Nickname,
                SenderFaceURL:    customer.Avatar,
                SenderPlatformID: constant.PLATFORMID_WEB,
                Content: &amp;request.TextContent{
                        Text: fmt.Sprintf("{\"content\":\"%s\"}", question),
                },
                ContentType:     constant.CONTENT_TYPE_TEXT,
                SessionType:     constant.SESSION_TYPE_SINGLE_CHAT,
                IsOnlineOnly:    false,
                NotOfflinePush:  false,
                OfflinePushInfo: &amp;request.OfflinePushInfo{},
        }
        res, err := json.Marshal(msgInfo)
        if err != nil </span><span class="cov0" title="0">{
                return errors.Wrapf(err, "marshal msgInfo failed")
        }</span>
        <span class="cov0" title="0">log.Debugf("msgInfo", string(res))
        host := fmt.Sprintf("http://%s", net.JoinHostPort(config.Config.OpenIM.Ip, fmt.Sprintf("%d", config.Config.OpenIM.ApiPort)))
        resp, err := msg.AdminSendMsg(msgInfo, "sendMsg:"+uid, host, token.Token)
        if err != nil </span><span class="cov0" title="0">{
                return errors.Wrapf(err, "send msg failed")
        }</span>
        <span class="cov0" title="0">log.Debugf("AdminSendMsg", "Resp: %+v", resp)

        return nil</span>
}
</pre>
		
		<pre class="file" id="file9" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "errors"
        "fmt"
        "net"

        "github.com/gin-gonic/gin"

        "github.com/OpenIMSDK/OpenKF/server/internal/common"
        "github.com/OpenIMSDK/OpenKF/server/internal/config"
        "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
        "github.com/OpenIMSDK/OpenKF/server/internal/models/base"
        systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles"
        requestparams "github.com/OpenIMSDK/OpenKF/server/internal/params/request"
        responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
        internal_utils "github.com/OpenIMSDK/OpenKF/server/internal/utils"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/auth"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/user"
        "github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)

// UserService user service.
type UserService struct {
        Service

        SysUserDao      *dao.SysUserDao
        SysCommunityDao *dao.SysCommunityDao
}

// NewUserService return new service with gin context.
func NewUserService(c *gin.Context) *UserService <span class="cov0" title="0">{
        return &amp;UserService{
                Service: Service{
                        ctx: c,
                },
                SysUserDao:      dao.NewSysUserDao(),
                SysCommunityDao: dao.NewSysCommunityDao(),
        }
}</span>

// CreateAdmin create admin user.
func (svc *UserService) CreateAdmin(user *requestparams.RegisterAdminParams) (string, uint, error) <span class="cov0" title="0">{
        // Check code
        mService := NewMailService((svc.ctx).(*gin.Context))
        if isExist := mService.CheckCode(user.UserInfo.Email, user.Code); !isExist </span><span class="cov0" title="0">{
                return "", 0, errors.New("code is not valid")
        }</span>

        // Create community
        <span class="cov0" title="0">communityParam := user.CommunityInfo
        cService := NewCommunityService((svc.ctx).(*gin.Context))
        _, cid, err := cService.Create(&amp;communityParam)
        if err != nil </span><span class="cov0" title="0">{
                return "", 0, err
        }</span>

        // Create admin
        <span class="cov0" title="0">uuid := utils.GenUUIDWithoutHyphen()
        adminParam := user.UserInfo
        admin := &amp;systemroles.SysUser{
                UserBase: base.UserBase{
                        UUID:     uuid,
                        Email:    adminParam.Email,
                        Nickname: adminParam.Nickname,
                        Avatar:   *adminParam.Avatar,
                        IsEnable: true,
                },
                IsAdmin:     true,
                Password:    utils.EncryptPassword(adminParam.Password),
                CommunityId: cid,
        }
        if err = svc.SysUserDao.Create(admin); err != nil </span><span class="cov0" title="0">{
                return uuid, 0, err
        }</span>

        <span class="cov0" title="0">u, _ := svc.SysUserDao.FindFirstByUUID(uuid)

        // TODO: set pipline to tx.
        param := &amp;request.RegisterUserParams{
                Secret: config.Config.OpenIM.Secret,
                Users: []request.User{
                        {
                                UserID:   uuid,
                                Nickname: adminParam.Nickname,
                                FaceURL:  "", // Use OpenKF avatar
                        },
                },
        }
        ok, err := registerUserToOpenIM(param)
        if err != nil || !ok </span><span class="cov0" title="0">{
                // Assume that the user has been created/deleted successfully
                _ = svc.SysUserDao.Delete(u)

                return uuid, u.Id, err
        }</span>

        <span class="cov0" title="0">return uuid, u.Id, nil</span>
}

// CreateStaff create staff user.
func (svc *UserService) CreateStaff(cid string, user *requestparams.RegisterStaffParams) (string, uint, error) <span class="cov0" title="0">{
        // Create staff
        uid := utils.GenUUIDWithoutHyphen()

        communityInfo, err := svc.SysCommunityDao.FindFirstByUUID(cid)
        if err != nil </span><span class="cov0" title="0">{
                return "", 0, err
        }</span>

        <span class="cov0" title="0">staffParam := user.UserInfo
        staff := &amp;systemroles.SysUser{
                UserBase: base.UserBase{
                        UUID:     uid,
                        Email:    staffParam.Email,
                        Nickname: staffParam.Nickname,
                        Avatar:   *staffParam.Avatar,
                        IsEnable: true,
                },
                IsAdmin:     false,
                Password:    utils.EncryptPassword(staffParam.Password),
                CommunityId: communityInfo.Id,
        }
        if err = svc.SysUserDao.Create(staff); err != nil </span><span class="cov0" title="0">{
                return uid, 0, err
        }</span>

        // TODO: Send email to staff

        <span class="cov0" title="0">u, _ := svc.SysUserDao.FindFirstByUUID(uid)

        // TODO: set pipline to tx.
        param := &amp;request.RegisterUserParams{
                Secret: config.Config.OpenIM.Secret,
                Users: []request.User{
                        {
                                UserID:   uid,
                                Nickname: staffParam.Nickname,
                                FaceURL:  "", // Use OpenKF avatar
                        },
                },
        }
        ok, err := registerUserToOpenIM(param)
        if err != nil || !ok </span><span class="cov0" title="0">{
                // Assume that the user has been created/deleted successfully
                _ = svc.SysUserDao.Delete(u)

                return uid, u.Id, err
        }</span>

        <span class="cov0" title="0">return uid, u.Id, nil</span>
}

// registerUserToOpenIM register user to openim.
func registerUserToOpenIM(param *request.RegisterUserParams) (bool, error) <span class="cov0" title="0">{
        // TODO: Add get operationID

        // Default not use tls/ssl
        host := fmt.Sprintf("http://%s", net.JoinHostPort(config.Config.OpenIM.Ip, fmt.Sprintf("%d", config.Config.OpenIM.ApiPort)))
        resp, err := user.RegisterUser(param, "registerUserToOpenIM", host)
        if err != nil </span><span class="cov0" title="0">{
                return false, err
        }</span>

        <span class="cov0" title="0">if resp.ErrCode != 0 </span><span class="cov0" title="0">{
                return false, errors.New(resp.ErrMsg)
        }</span>

        <span class="cov0" title="0">return true, nil</span>
}

// DeleteStaff delete staff user.
func (svc *UserService) DeleteStaff(uid string) error <span class="cov0" title="0">{
        if uid == "" </span><span class="cov0" title="0">{
                return common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return err
        }</span>

        <span class="cov0" title="0">err = svc.SysUserDao.Delete(u)
        if err != nil </span><span class="cov0" title="0">{
                return err
        }</span>

        <span class="cov0" title="0">return nil</span>
}

// LoginWithAccount login with account.
func (svc *UserService) LoginWithAccount(param *requestparams.LoginParamsWithAccount) (*responseparams.UserTokenResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.UserTokenResponse{}

        // Check user
        u, err := svc.SysUserDao.FindFirstByEmail(param.Email)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Check password
        <span class="cov0" title="0">if !utils.ComparePassword(param.Password, u.Password) </span><span class="cov0" title="0">{
                return resp, errors.New("password is not correct")
        }</span>

        // Get community id
        <span class="cov0" title="0">cService := NewCommunityService((svc.ctx).(*gin.Context))
        c, err := cService.GetCommunityInfoById(u.CommunityId)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Generate KF token
        <span class="cov0" title="0">kfToken, kfExpireTimeSeconds, err := internal_utils.GenerateJwtToken(u.UUID, c.UUID)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Get IM token
        <span class="cov0" title="0">imParam := &amp;request.UserTokenParams{
                Secret:     config.Config.OpenIM.Secret,
                UserID:     u.UUID,
                PlatformID: uint(config.Config.OpenIM.PlatformID),
        }
        imResp, err := getUserIMToken(imParam)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Fill response data
        <span class="cov0" title="0">resp.UUID = u.UUID
        resp.KFToken = &amp;responseparams.TokenResponse{
                Token:             kfToken,
                ExpireTimeSeconds: kfExpireTimeSeconds,
        }
        resp.IMToken = &amp;responseparams.TokenResponse{
                Token:             imResp.Token,
                ExpireTimeSeconds: imResp.ExpireTimeSeconds,
        }

        // TODO: Set Online in OpenIM or do this in js-sdk

        return resp, nil</span>
}

// getUserIMToken get user im token.
func getUserIMToken(param *request.UserTokenParams) (*response.TokenData, error) <span class="cov0" title="0">{
        // TODO: Add operationID

        // Default not use tls/ssl
        host := fmt.Sprintf("http://%s", net.JoinHostPort(config.Config.OpenIM.Ip, fmt.Sprintf("%d", config.Config.OpenIM.ApiPort)))
        resp, err := auth.GetUserToken(param, "getUserIMToken", host)
        if err != nil </span><span class="cov0" title="0">{
                return &amp;response.TokenData{}, err
        }</span>

        <span class="cov0" title="0">if resp.ErrCode != 0 </span><span class="cov0" title="0">{
                return &amp;response.TokenData{}, errors.New(resp.ErrMsg)
        }</span>

        <span class="cov0" title="0">return &amp;resp.Data, nil</span>
}

// GetAdminToken get admin token.
func (svc *UserService) GetAdminToken() (*response.TokenData, error) <span class="cov0" title="0">{
        params := &amp;request.UserTokenParams{
                Secret:     config.Config.OpenIM.Secret,
                PlatformID: uint(config.Config.OpenIM.PlatformID),
                UserID:     config.Config.OpenIM.AdminID,
        }

        // TODO: Get cache from redis

        return getUserIMToken(params)
}</span>

// GetUserInfoByUUID get user info by uuid.
func (svc *UserService) GetUserInfoByUUID(uid string) (*responseparams.UserInfoResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.UserInfoResponse{}

        if uid == "" </span><span class="cov0" title="0">{
                return resp, common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        <span class="cov0" title="0">resp.UUID = u.UUID
        resp.Email = u.Email
        resp.Nickname = u.Nickname
        resp.Avatar = u.Avatar
        resp.Description = u.Description
        resp.IsAdmin = u.IsAdmin
        resp.IsEnable = u.IsEnable
        resp.CreatedAt = u.CreatedAt.Format("2006-01-02 15:04:05")

        return resp, nil</span>
}

// UpdateUserInfo update user info.
func (svc *UserService) UpdateUserInfo(uid string, params *requestparams.UpdateUserInfoParams) (*responseparams.UserInfoResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.UserInfoResponse{}

        if uid == "" </span><span class="cov0" title="0">{
                return resp, common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Update user info
        <span class="cov0" title="0">if params.Nickname != nil </span><span class="cov0" title="0">{
                u.Nickname = *params.Nickname
        }</span>
        <span class="cov0" title="0">if params.Description != nil </span><span class="cov0" title="0">{
                u.Description = *params.Description
        }</span>
        <span class="cov0" title="0">if params.Avatar != nil </span><span class="cov0" title="0">{
                u.Avatar = *params.Avatar
        }</span>
        <span class="cov0" title="0">if params.Email != nil </span><span class="cov0" title="0">{
                u.Email = *params.Email
        }</span>

        // TODO: Check if the user is admin
        <span class="cov0" title="0">if params.IsEnable != nil </span><span class="cov0" title="0">{
                u.IsEnable = *params.IsEnable
        }</span>
        <span class="cov0" title="0">if params.IsAdmin != nil </span><span class="cov0" title="0">{
                u.IsAdmin = *params.IsAdmin
        }</span>

        // Update user info
        <span class="cov0" title="0">if err = svc.SysUserDao.Update(u); err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Get new info
        <span class="cov0" title="0">u, _ = svc.SysUserDao.FindFirstByUUID(uid)
        resp.UUID = u.UUID
        resp.Email = u.Email
        resp.Nickname = u.Nickname
        resp.Avatar = u.Avatar
        resp.Description = u.Description
        resp.IsAdmin = u.IsAdmin
        resp.IsEnable = u.IsEnable
        resp.CreatedAt = u.CreatedAt.Format("2006-01-02 15:04:05")

        return resp, nil</span>
}

// UpdateUserPassword update user password.
func (svc *UserService) UpdateUserPassword(uid string, params *requestparams.UpdateUserPasswordParams) error <span class="cov0" title="0">{
        if uid == "" </span><span class="cov0" title="0">{
                return common.NewError(common.I_INVALID_PARAM)
        }</span>

        <span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
        if err != nil </span><span class="cov0" title="0">{
                return err
        }</span>

        // Update user info
        <span class="cov0" title="0">if params.Password != "" &amp;&amp; params.RepeatPassword != "" &amp;&amp;
                params.Password == params.RepeatPassword </span><span class="cov0" title="0">{
                u.Password = utils.EncryptPassword(params.Password)
        }</span>

        // Update user info
        <span class="cov0" title="0">if err = svc.SysUserDao.Update(u); err != nil </span><span class="cov0" title="0">{
                return err
        }</span>

        <span class="cov0" title="0">return nil</span>
}

// GetCommunityUserList get community user list.
func (svc *UserService) GetCommunityUserList(cid string, params *requestparams.ListPageParams) (*responseparams.ListPageResponse, error) <span class="cov0" title="0">{
        resp := &amp;responseparams.ListPageResponse{}
        userInfos := make([]*responseparams.UserInfoResponse, 0)

        csvc := NewCommunityService((svc.ctx).(*gin.Context))
        info, err := csvc.GetCommunityInfoByUUIDV2(cid)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Get user list
        <span class="cov0" title="0">users, total, err := svc.SysUserDao.FindByCommunityIdPage(info.Id, (params.Page-1)*params.PageSize, params.PageSize)
        if err != nil </span><span class="cov0" title="0">{
                return resp, err
        }</span>

        // Fill response data
        <span class="cov0" title="0">for _, u := range users </span><span class="cov0" title="0">{
                userInfos = append(userInfos, &amp;responseparams.UserInfoResponse{
                        UUID:        u.UUID,
                        Email:       u.Email,
                        Nickname:    u.Nickname,
                        Avatar:      u.Avatar,
                        Description: u.Description,
                        IsAdmin:     u.IsAdmin,
                        IsEnable:    u.IsEnable,
                        CreatedAt:   u.CreatedAt.Format("2006-01-02 15:04:05"),
                })
        }</span>

        <span class="cov0" title="0">resp.List = userInfos
        resp.Page = params.Page
        resp.PageSize = params.PageSize
        resp.Total = int(total)

        return resp, nil</span>
}
</pre>
		
		<pre class="file" id="file10" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
        "context"
        "errors"

        "github.com/shomali11/slacker"

        "github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
)

// user dispatch queue key.
const (
        USER_DISPATCH_QUEUE_KEY = "openkf:user_dispatch_queue"
        USER_SLACK_MAP_KEY      = "openkf:user_slack_map"
)

// UserDispatchService user service.
type UserDispatchService struct {
        Service

        UserDispatchDao *dao.UserDispatchDao
        SysUserDao      *dao.SysUserDao
}

// NewUserDispatchService return new service with context.
func NewUserDispatchService(c context.Context) *UserDispatchService <span class="cov8" title="1">{
        return &amp;UserDispatchService{
                Service: Service{
                        ctx: c,
                },

                UserDispatchDao: dao.NewUserDispatchDao(),
                SysUserDao:      dao.NewSysUserDao(),
        }
}</span>

// AddUser add user to enqueue.
func (s *UserDispatchService) AddUser(uuid string) error <span class="cov0" title="0">{
        _, err := s.UserDispatchDao.AddUser(USER_DISPATCH_QUEUE_KEY, uuid)

        return err
}</span>

// GetUser get user and update timestamp.
func (s *UserDispatchService) GetUser() (string, error) <span class="cov8" title="1">{
        res, err := s.UserDispatchDao.GetUser(USER_DISPATCH_QUEUE_KEY)
        if err != nil </span><span class="cov0" title="0">{
                return "", errors.New("can not find a user")
        }</span>

        // return a default value if res is empty
        <span class="cov8" title="1">if res == "" </span><span class="cov0" title="0">{
                u, err := s.SysUserDao.First()
                if err != nil </span><span class="cov0" title="0">{
                        return "", err
                }</span>

                <span class="cov0" title="0">return u.UUID, nil</span>
        }

        <span class="cov8" title="1">return res, nil</span>
}

// DeleteUser delete user from queue.
func (s *UserDispatchService) DeleteUser(uuid string) error <span class="cov0" title="0">{
        _, err := s.UserDispatchDao.RemoveUser(USER_DISPATCH_QUEUE_KEY, uuid)

        return err
}</span>

// SetSlackMap set slack map.
func (s *UserDispatchService) SetSlackMap(customID, staffID string, botContext slacker.BotContext) error <span class="cov0" title="0">{
        return s.UserDispatchDao.SetSlackMap(USER_SLACK_MAP_KEY, customID, staffID, botContext.Event().ChannelID)
}</span>

// GetSlackMap get slack map.
func (s *UserDispatchService) GetSlackMap(customID string) *dao.SlackMap <span class="cov0" title="0">{
        return s.UserDispatchDao.GetSlackMap(USER_SLACK_MAP_KEY, customID)
}</span>

// GetSlackIDs get all staff id.
func (s *UserDispatchService) GetSlackIDs() ([]string, error) <span class="cov0" title="0">{
        return s.UserDispatchDao.GetSlackIDs(USER_SLACK_MAP_KEY)
}</span>

// SlackUserFilter filter user by slack id.
func (s *UserDispatchService) SlackUserFilter(uid string) bool <span class="cov0" title="0">{
        ids, err := s.GetSlackIDs()
        if err != nil || len(ids) == 0 </span><span class="cov0" title="0">{
                return false
        }</span>

        <span class="cov0" title="0">for _, id := range ids </span><span class="cov0" title="0">{
                if id == uid </span><span class="cov0" title="0">{
                        return true
                }</span>
        }

        <span class="cov0" title="0">return false</span>
}
</pre>
		
		<pre class="file" id="file11" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package auth

import (
        "fmt"

        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/client"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
)

const (
        // PATH_USER_TOKEN get user token path.
        PATH_USER_TOKEN = "/auth/user_token"
)

// GetUserToken get user token from openim server.
func GetUserToken(param *request.UserTokenParams, operationID, host string) (*response.UserTokenResponse, error) <span class="cov8" title="1">{
        // host: http://ip:port
        url := fmt.Sprintf("%s%s", host, PATH_USER_TOKEN)

        r := &amp;response.UserTokenResponse{}
        client := client.NewClient(url)
        resp, err := client.POST(operationID, "", param)
        if err != nil </span><span class="cov0" title="0">{
                return r, err
        }</span>

        <span class="cov8" title="1">r.ErrCode = uint(resp["errCode"].(float64))
        r.ErrMsg = resp["errMsg"].(string)
        r.ErrDlt = resp["errDlt"].(string)
        if data, ok := resp["data"]; ok </span><span class="cov8" title="1">{
                data := data.(map[string]interface{})
                r.Data.Token = data["token"].(string)
                r.Data.ExpireTimeSeconds = uint(data["expireTimeSeconds"].(float64))
        }</span>

        <span class="cov8" title="1">return r, nil</span>
}
</pre>
		
		<pre class="file" id="file12" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package msg

import (
        "errors"
        "fmt"

        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/client"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
)

const (
        // PATH_SEND_MSG admin send message.
        PATH_SEND_MSG = "/msg/send_msg"
)

// AdminSendMsg admin send message.
func AdminSendMsg(param *request.MsgInfo, operationID, host, adminToken string) (*response.BaseResponse, error) <span class="cov8" title="1">{
        // host: http://ip:port
        url := fmt.Sprintf("%s%s", host, PATH_SEND_MSG)

        r := &amp;response.BaseResponse{}
        client := client.NewClient(url)
        resp, err := client.POST(operationID, adminToken, param)
        if err != nil </span><span class="cov0" title="0">{
                return r, err
        }</span>

        <span class="cov8" title="1">r.ErrCode = uint(resp["errCode"].(float64))
        r.ErrMsg = resp["errMsg"].(string)
        r.ErrDlt = resp["errDlt"].(string)

        if resp["data"] == nil </span><span class="cov8" title="1">{
                return r, errors.New("data is nil: " + r.ErrMsg)
        }</span>

        <span class="cov0" title="0">r.Data = resp["data"]

        return r, nil</span>
}
</pre>
		
		<pre class="file" id="file13" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package user

import (
        "fmt"

        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/client"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
        "github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
)

const (
        // PATH_USER_REGISTER register user path.
        PATH_USER_REGISTER = "/user/user_register"
)

// RegisterUser register user.
func RegisterUser(param *request.RegisterUserParams, operationID, host string) (*response.BaseResponse, error) <span class="cov8" title="1">{
        // host: http://ip:port
        url := fmt.Sprintf("%s%s", host, PATH_USER_REGISTER)

        r := &amp;response.BaseResponse{}
        client := client.NewClient(url)
        resp, err := client.POST(operationID, "", param)
        if err != nil </span><span class="cov0" title="0">{
                return r, err
        }</span>

        <span class="cov8" title="1">r.ErrCode = uint(resp["errCode"].(float64))
        r.ErrMsg = resp["errMsg"].(string)
        r.ErrDlt = resp["errDlt"].(string)

        return r, nil</span>
}
</pre>
		
		<pre class="file" id="file14" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
        "crypto/hmac"
        "crypto/md5"
        "crypto/sha256"
        "encoding/hex"
)

const (
        _salt = "openkf"
)

// EncryptPassword encrypt password.
func EncryptPassword(password string) string <span class="cov8" title="1">{
        // md5
        m := md5.New()
        m.Write([]byte(password + _salt))
        mByte := m.Sum(nil)

        // hmac
        h := hmac.New(sha256.New, []byte(_salt))
        h.Write(mByte)
        password = hex.EncodeToString(h.Sum(nil))

        return password
}</span>

// ComparePassword compare password.
func ComparePassword(password, encryptedPassword string) bool <span class="cov8" title="1">{
        return EncryptPassword(password) == encryptedPassword
}</span>
</pre>
		
		<pre class="file" id="file15" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import "strconv"

// IntToString convert int to string.
func IntToString(i interface{}) string <span class="cov8" title="1">{
        return strconv.FormatInt(int64(i.(int)), 10)
}</span>

// StringToInt convert string to int.
func StringToInt(i string) int <span class="cov8" title="1">{
        j, _ := strconv.Atoi(i)
        return j
}</span>
</pre>
		
		<pre class="file" id="file16" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
        "fmt"

        "github.com/gofrs/uuid"
)

// GenUUID generate uuid.
func GenUUID() string <span class="cov8" title="1">{
        return uuid.Must(uuid.NewV4()).String()
}</span>

// GenUUIDWithoutHyphen generate uuid without hyphen.
func GenUUIDWithoutHyphen() string <span class="cov8" title="1">{
        return toString(uuid.Must(uuid.NewV4()))
}</span>

// encodeCanonical encodes the canonical RFC-4122 form of UUID u into the
// first 36 bytes dst.
func encodeCanonical(dst []byte, u uuid.UUID) <span class="cov8" title="1">{
        const hextable = "0123456789abcdef"
        dst[8] = '-'
        dst[13] = '-'
        dst[18] = '-'
        dst[23] = '-'
        for i, x := range [16]byte{
                0, 2, 4, 6,
                9, 11,
                14, 16,
                19, 21,
                24, 26, 28, 30, 32, 34,
        } </span><span class="cov8" title="1">{
                c := u[i]
                dst[x] = hextable[c&gt;&gt;4]
                dst[x+1] = hextable[c&amp;0x0f]
        }</span>
}

// String returns a canonical RFC-4122 string representation of the UUID:
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, but delete separator -.
func toString(u uuid.UUID) string <span class="cov8" title="1">{
        var buf [36]byte
        encodeCanonical(buf[:], u)
        return fmt.Sprintf("%s%s%s%s%s", buf[0:7], buf[9:12], buf[14:17], buf[19:22], buf[24:])
}</span>
</pre>
		
		<pre class="file" id="file17" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import "regexp"

// IsValidEmail check if the email is valid.
func IsValidEmail(email string) bool <span class="cov8" title="1">{
        // Regular expression for basic email validation
        // This regex pattern is a simplified version and may not cover all edge cases.
        // You can use more comprehensive patterns depending on your specific needs.
        emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`

        return regexp.MustCompile(emailRegex).MatchString(email)
}</span>
</pre>
		
		</div>
	</body>
	<script>
	(function() {
		var files = document.getElementById('files');
		var visible;
		files.addEventListener('change', onChange, false);
		function select(part) {
			if (visible)
				visible.style.display = 'none';
			visible = document.getElementById(part);
			if (!visible)
				return;
			files.value = part;
			visible.style.display = 'block';
			location.hash = part;
		}
		function onChange() {
			select(files.value);
			window.scrollTo(0, 0);
		}
		if (location.hash != "") {
			select(location.hash.substr(1));
		}
		if (!visible) {
			select("file0");
		}
	})();
	</script>
</html>

Copy link
Collaborator

@IRONICBo IRONICBo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@cubxxw cubxxw merged commit a4d841c into openimsdk:main Sep 7, 2023
9 of 10 checks passed
@openimsdk openimsdk locked and limited conversation to collaborators Sep 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature: Support unit test in go.
4 participants