TTW_Bot_GO/internal/platforms/twitch_auth.go

134 lines
4.0 KiB
Go

package platforms
import (
"context"
"fmt"
"net/http"
"stream-bot/internal/logger"
"time"
)
type TwitchAuth struct {
clientID string
clientSecret string
redirectURI string
server *http.Server
waitCh chan string
}
func NewTwitchAuth(clientID, clientSecret string) *TwitchAuth {
return &TwitchAuth{
clientID: clientID,
clientSecret: clientSecret,
redirectURI: "http://localhost:8089",
waitCh: make(chan string, 1),
}
}
func (ta *TwitchAuth) GenerateAuthURL(scope []string, state string) string {
url := "https://id.twitch.tv/oauth2/authorize?" +
"client_id=" + ta.clientID +
"&redirect_uri=" + ta.redirectURI +
"&response_type=token" +
"&scope=" + scopeString(scope) +
"&state=" + state
return url
}
func scopeString(scopes []string) string {
s := ""
for i, sc := range scopes {
if i > 0 {
s += "+"
}
s += sc
}
return s
}
func (ta *TwitchAuth) StartTempServer() error {
if ta.server != nil {
return nil
}
mux := http.NewServeMux()
mux.HandleFunc("/", ta.handleCallback)
ta.server = &http.Server{
Addr: ":8089",
Handler: mux,
}
go func() {
if err := ta.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Error("OAuth server error: %v", err)
}
}()
return nil
}
func (ta *TwitchAuth) StopTempServer() {
if ta.server != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ta.server.Shutdown(ctx)
ta.server = nil
}
}
func (ta *TwitchAuth) handleCallback(w http.ResponseWriter, r *http.Request) {
html := `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Twitch Auth</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 50px; }
.success { color: green; }
.error { color: red; }
</style>
</head>
<body>
<h3>Авторизация Twitch</h3>
<p>Обработка токена...</p>
<script>
const hash = window.location.hash.substring(1);
const params = new URLSearchParams(hash);
const accessToken = params.get('access_token');
const state = params.get('state');
if (accessToken && state) {
fetch('http://localhost:8080/api/platforms/twitch/auth/callback?token=' + encodeURIComponent(accessToken) + '&state=' + encodeURIComponent(state))
.then(res => {
if (res.ok) return res.text();
throw new Error('Server error: ' + res.status);
})
.then(data => {
document.body.innerHTML = '<h3 class="success">✅ Токен успешно сохранён! Теперь можно закрыть эту вкладку.</h3>';
})
.catch(err => {
document.body.innerHTML = '<h3 class="error">❌ Ошибка сохранения токена: ' + err.message + '</h3><p>Пожалуйста, закройте это окно и проверьте настройки в боте.</p>';
});
} else {
document.body.innerHTML = '<h3 class="error">❌ Токен не получен или отсутствует state</h3>';
}
</script>
</body>
</html>
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write([]byte(html))
}
func (ta *TwitchAuth) WaitForToken(timeout time.Duration) (string, error) {
select {
case token := <-ta.waitCh:
return token, nil
case <-time.After(timeout):
return "", fmt.Errorf("timeout waiting for token")
}
}
func (ta *TwitchAuth) SetTokenCallback(token string) {
select {
case ta.waitCh <- token:
default:
}
}