AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
144
skills/openclaw-self-healing/scripts/kakao-oauth-refresh.js
Normal file
144
skills/openclaw-self-healing/scripts/kakao-oauth-refresh.js
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Kakao OAuth Refresh Token Collector
|
||||
*
|
||||
* 1. Starts local server on :8080
|
||||
* 2. Opens browser for authorization
|
||||
* 3. Exchanges code for tokens
|
||||
* 4. Saves refresh_token to openclaw.json
|
||||
*/
|
||||
|
||||
const http = require('http');
|
||||
const { spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const REST_API_KEY = process.env.KAKAO_REST_API_KEY || '4d7f36bbfa672c5e24582307de57f4e4';
|
||||
const CLIENT_SECRET = process.env.KAKAO_CLIENT_SECRET;
|
||||
const REDIRECT_URI = 'http://localhost:8080/callback';
|
||||
const SCOPE = 'talk_calendar';
|
||||
|
||||
if (!CLIENT_SECRET) {
|
||||
console.error('❌ KAKAO_CLIENT_SECRET environment variable not set');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const CONFIG_PATH = path.join(process.env.HOME, '.openclaw', 'openclaw.json');
|
||||
|
||||
// Authorization URL
|
||||
const authUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=${SCOPE}`;
|
||||
|
||||
console.log('📱 Kakao OAuth Refresh Token Collector\n');
|
||||
console.log('🔗 Authorization URL:');
|
||||
console.log(authUrl);
|
||||
console.log('\n🌐 Opening browser...\n');
|
||||
|
||||
// Open browser
|
||||
spawn('open', [authUrl]);
|
||||
|
||||
// Start callback server
|
||||
const server = http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url, `http://localhost:8080`);
|
||||
|
||||
if (url.pathname === '/callback') {
|
||||
const code = url.searchParams.get('code');
|
||||
const error = url.searchParams.get('error');
|
||||
|
||||
if (error) {
|
||||
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end(`<h1>❌ Authorization Failed</h1><p>Error: ${error}</p>`);
|
||||
console.error('❌ Authorization failed:', error);
|
||||
server.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end('<h1>❌ No code received</h1>');
|
||||
console.error('❌ No authorization code received');
|
||||
server.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('✅ Authorization code received:', code);
|
||||
console.log('🔄 Exchanging code for tokens...\n');
|
||||
|
||||
// Exchange code for tokens
|
||||
try {
|
||||
const tokenResponse = await fetch('https://kauth.kakao.com/oauth/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
client_id: REST_API_KEY,
|
||||
client_secret: CLIENT_SECRET,
|
||||
redirect_uri: REDIRECT_URI,
|
||||
code: code
|
||||
})
|
||||
});
|
||||
|
||||
const tokens = await tokenResponse.json();
|
||||
|
||||
if (tokens.error) {
|
||||
throw new Error(`Token exchange failed: ${tokens.error_description || tokens.error}`);
|
||||
}
|
||||
|
||||
console.log('✅ Tokens received:');
|
||||
console.log(` Access Token: ${tokens.access_token.substring(0, 20)}...`);
|
||||
console.log(` Refresh Token: ${tokens.refresh_token.substring(0, 20)}...`);
|
||||
console.log(` Expires in: ${tokens.expires_in}s (${tokens.expires_in / 3600}h)`);
|
||||
console.log(` Refresh Token Expires in: ${tokens.refresh_token_expires_in}s (${tokens.refresh_token_expires_in / 86400}d)\n`);
|
||||
|
||||
// Save to openclaw.json
|
||||
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
||||
|
||||
if (!config.env) config.env = {};
|
||||
if (!config.env.vars) config.env.vars = {};
|
||||
|
||||
config.env.vars.KAKAO_ACCESS_TOKEN = tokens.access_token;
|
||||
config.env.vars.KAKAO_REFRESH_TOKEN = tokens.refresh_token;
|
||||
config.env.vars.KAKAO_TOKEN_EXPIRES_AT = new Date(Date.now() + tokens.expires_in * 1000).toISOString();
|
||||
config.env.vars.KAKAO_REFRESH_TOKEN_EXPIRES_AT = new Date(Date.now() + tokens.refresh_token_expires_in * 1000).toISOString();
|
||||
|
||||
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
||||
|
||||
console.log('💾 Tokens saved to:', CONFIG_PATH);
|
||||
console.log('✅ Done! Gateway restart required.\n');
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end(`
|
||||
<h1>✅ Success!</h1>
|
||||
<p>Refresh token saved. You can close this window.</p>
|
||||
<p>Next: Restart OpenClaw gateway.</p>
|
||||
`);
|
||||
|
||||
server.close();
|
||||
process.exit(0);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Token exchange failed:', error.message);
|
||||
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end(`<h1>❌ Token Exchange Failed</h1><p>${error.message}</p>`);
|
||||
server.close();
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end('Not Found');
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(8080, () => {
|
||||
console.log('🚀 Callback server listening on http://localhost:8080');
|
||||
console.log('⏳ Waiting for authorization...\n');
|
||||
});
|
||||
|
||||
// Timeout after 5 minutes
|
||||
setTimeout(() => {
|
||||
console.log('\n⏰ Timeout after 5 minutes');
|
||||
server.close();
|
||||
process.exit(1);
|
||||
}, 5 * 60 * 1000);
|
||||
Reference in New Issue
Block a user