user-center-slack/client.go (142 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 slack_user_center import ( "encoding/json" "fmt" "io" "net/http" "net/url" "github.com/apache/answer/plugin" "github.com/go-resty/resty/v2" "github.com/segmentfault/pacman/log" ) type SlackClient struct { AccessToken string ClientID string ClientSecret string RedirectURI string AuthedUserID string UserInfoMapping map[string]*UserInfo ChannelMapping string } func NewSlackClient(clientID, clientSecret string) *SlackClient { return &SlackClient{ ClientID: clientID, ClientSecret: clientSecret, } } // OAuthV2ResponseTeam type OAuthV2ResponseTeam struct { ID string `json:"id"` Name string `json:"name"` } // OAuthResponseIncomingWebhook type OAuthResponseIncomingWebhook struct { URL string `json:"url"` Channel string `json:"channel"` ChannelID string `json:"channel_id,omitempty"` ConfigurationURL string `json:"configuration_url"` } // OAuthV2ResponseEnterprise type OAuthV2ResponseEnterprise struct { ID string `json:"id"` Name string `json:"name"` } // OAuthV2ResponseAuthedUser type OAuthV2ResponseAuthedUser struct { ID string `json:"id"` Scope string `json:"scope"` AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` TokenType string `json:"token_type"` } type TokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` Scope string `json:"scope"` BotUserID string `json:"bot_user_id"` AppID string `json:"app_id"` Team OAuthV2ResponseTeam `json:"team"` IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"` Enterprise OAuthV2ResponseEnterprise `json:"enterprise"` IsEnterpriseInstall bool `json:"is_enterprise_install"` AuthedUser OAuthV2ResponseAuthedUser `json:"authed_user"` RefreshToken string `json:"refresh_token"` ExpiresIn int `json:"expires_in"` Error string `json:"error,omitempty"` } type Member struct { ID string `json:"id"` Name string `json:"name"` TeamID string `json:"team_id"` } // ExchangeCodeForUser through OAuthToken func (sc *SlackClient) AuthUser(code string) (info *UserInfo, err error) { clientID := sc.ClientID clientSecret := sc.ClientSecret redirectURI := fmt.Sprintf("%s/answer/api/v1/user-center/login/callback", plugin.SiteURL()) data := url.Values{} data.Set("code", code) data.Set("client_id", clientID) data.Set("client_secret", clientSecret) data.Set("redirect_uri", redirectURI) resp, err := http.PostForm("https://slack.com/api/oauth.v2.access", data) if err != nil { log.Errorf("Failed to exchange code for token: %v", err) return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("Failed to read response body: %v", err) } var tokenResp TokenResponse err = json.Unmarshal([]byte(body), &tokenResp) if err != nil { fmt.Println("Error parsing response:", err) return nil, err } if tokenResp.Error != "" { return nil, fmt.Errorf("Slack API error in AuthUser: %s", tokenResp.Error) } sc.AccessToken = tokenResp.AccessToken sc.AuthedUserID = tokenResp.AuthedUser.ID return sc.GetUserDetailInfo(sc.AuthedUserID) } func (sc *SlackClient) GetUserDetailInfo(userid string) (info *UserInfo, err error) { getUserInfoResp, err := resty.New().R(). SetHeader("Authorization", fmt.Sprintf("Bearer %s", sc.AccessToken)). SetHeader("Accept", "application/json"). Get("https://slack.com/api/users.info?user=" + userid) if err != nil { log.Errorf("Failed to get user info: %v", err) return nil, err } var authUserResp *AuthUserResp err = json.Unmarshal([]byte(getUserInfoResp.String()), &authUserResp) if err != nil { log.Errorf("Error unmarshaling user info: %v", err) return nil, err } if !authUserResp.Ok { log.Errorf("Failed to get valid user info, Slack API error: %s", authUserResp.Errmsg) return nil, fmt.Errorf("Get user info failed: %s", authUserResp.Errmsg) } log.Debugf("Get user info for UserID: %s", userid) if authUserResp.User == nil { log.Errorf("No user data available in the response") return nil, fmt.Errorf("No user data available in the response") } authUserResp.User.IsAvailable = true authUserResp.User.Status = 1 // Directly returning the user data parsed from the response return authUserResp.User, nil } func (sc *SlackClient) UpdateUserInfo() (err error) { log.Debug("Try to update slack client") userInfo, err := sc.GetUserDetailInfo(sc.AuthedUserID) if err != nil { log.Errorf("Failed to update user info: %v", err) return err } if sc.UserInfoMapping == nil { sc.UserInfoMapping = make(map[string]*UserInfo) } sc.UserInfoMapping[sc.AuthedUserID] = userInfo log.Infof("Updated user info for UserID: %s", sc.AuthedUserID) return nil }