| /* |
| * 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 |
| } |