GitHub / Twitter連携の設定をDBに保存するように
This commit is contained in:
		
							parent
							
								
									5675ecead9
								
							
						
					
					
						commit
						cb6f390fb6
					
				
					 12 changed files with 632 additions and 476 deletions
				
			
		|  | @ -57,13 +57,6 @@ npm install web-push -g | |||
| web-push generate-vapid-keys | ||||
| ``` | ||||
| 
 | ||||
| *(optional)* Create a twitter application | ||||
| ---------------------------------------------------------------- | ||||
| If you want to enable the twitter integration, you need to create a twitter app at [https://developer.twitter.com/en/apply/user](https://developer.twitter.com/en/apply/user). | ||||
| 
 | ||||
| In the app you need to set the oauth callback url as : https://misskey-instance/api/tw/cb | ||||
| 
 | ||||
| 
 | ||||
| *5.* Make configuration file | ||||
| ---------------------------------------------------------------- | ||||
| 1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`. | ||||
|  |  | |||
|  | @ -1095,6 +1095,16 @@ admin/views/instance.vue: | |||
|   enable-recaptcha: "reCAPTCHAを有効にする" | ||||
|   recaptcha-site-key: "reCAPTCHA site key" | ||||
|   recaptcha-secret-key: "reCAPTCHA secret key" | ||||
|   twitter-integration-config: "Twitter連携の設定" | ||||
|   twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。" | ||||
|   enable-twitter-integration: "Twitter連携を有効にする" | ||||
|   twitter-integration-consumer-key: "Consumer key" | ||||
|   twitter-integration-consumer-secret: "Consumer secret" | ||||
|   github-integration-config: "GitHub連携の設定" | ||||
|   github-integration-info: "コールバックURLは /api/gh/cb に設定します。" | ||||
|   enable-github-integration: "GitHub連携を有効にする" | ||||
|   github-integration-client-id: "Client ID" | ||||
|   github-integration-client-secret: "Client secret" | ||||
|   proxy-account-config: "プロキシアカウントの設定" | ||||
|   proxy-account-info: "プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがサーバーに配達されないため、代わりにプロキシアカウントがフォローするようにします。" | ||||
|   proxy-account-username: "プロキシアカウントのユーザー名" | ||||
|  |  | |||
|  | @ -53,6 +53,28 @@ | |||
| 			<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p> | ||||
| 		</section> | ||||
| 	</ui-card> | ||||
| 
 | ||||
| 	<ui-card> | ||||
| 		<div slot="title"><fa :icon="['fab', 'twitter']"/> %i18n:@twitter-integration-config%</div> | ||||
| 		<section> | ||||
| 			<ui-switch v-model="enableTwitterIntegration">%i18n:@enable-twitter-integration%</ui-switch> | ||||
| 			<ui-info>%i18n:@twitter-integration-info%</ui-info> | ||||
| 			<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@twitter-integration-consumer-key%</ui-input> | ||||
| 			<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@twitter-integration-consumer-secret%</ui-input> | ||||
| 			<ui-button @click="updateMeta">%i18n:@save%</ui-button> | ||||
| 		</section> | ||||
| 	</ui-card> | ||||
| 
 | ||||
| 	<ui-card> | ||||
| 		<div slot="title"><fa :icon="['fab', 'github']"/> %i18n:@github-integration-config%</div> | ||||
| 		<section> | ||||
| 			<ui-switch v-model="enableGithubIntegration">%i18n:@enable-github-integration%</ui-switch> | ||||
| 			<ui-info>%i18n:@github-integration-info%</ui-info> | ||||
| 			<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@github-integration-client-id%</ui-input> | ||||
| 			<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@github-integration-client-secret%</ui-input> | ||||
| 			<ui-button @click="updateMeta">%i18n:@save%</ui-button> | ||||
| 		</section> | ||||
| 	</ui-card> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
|  | @ -77,6 +99,12 @@ export default Vue.extend({ | |||
| 			enableRecaptcha: false, | ||||
| 			recaptchaSiteKey: null, | ||||
| 			recaptchaSecretKey: null, | ||||
| 			enableTwitterIntegration: false, | ||||
| 			twitterConsumerKey: null, | ||||
| 			twitterConsumerSecret: null, | ||||
| 			enableGithubIntegration: false, | ||||
| 			githubClientId: null, | ||||
| 			githubClientSecret: null, | ||||
| 			proxyAccount: null, | ||||
| 			inviteCode: null, | ||||
| 		}; | ||||
|  | @ -98,6 +126,12 @@ export default Vue.extend({ | |||
| 			this.recaptchaSiteKey = meta.recaptchaSiteKey; | ||||
| 			this.recaptchaSecretKey = meta.recaptchaSecretKey; | ||||
| 			this.proxyAccount = meta.proxyAccount; | ||||
| 			this.enableTwitterIntegration = meta.enableTwitterIntegration; | ||||
| 			this.twitterConsumerKey = meta.twitterConsumerKey; | ||||
| 			this.twitterConsumerSecret = meta.twitterConsumerSecret; | ||||
| 			this.enableGithubIntegration = meta.enableGithubIntegration; | ||||
| 			this.githubClientId = meta.githubClientId; | ||||
| 			this.githubClientSecret = meta.githubClientSecret; | ||||
| 		}); | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -131,6 +165,12 @@ export default Vue.extend({ | |||
| 				recaptchaSiteKey: this.recaptchaSiteKey, | ||||
| 				recaptchaSecretKey: this.recaptchaSecretKey, | ||||
| 				proxyAccount: this.proxyAccount, | ||||
| 				enableTwitterIntegration: this.enableTwitterIntegration, | ||||
| 				twitterConsumerKey: this.twitterConsumerKey, | ||||
| 				twitterConsumerSecret: this.twitterConsumerSecret, | ||||
| 				enableGithubIntegration: this.enableGithubIntegration, | ||||
| 				githubClientId: this.githubClientId, | ||||
| 				githubClientSecret: this.githubClientSecret, | ||||
| 			}).then(() => { | ||||
| 				this.$swal({ | ||||
| 					type: 'success', | ||||
|  |  | |||
|  | @ -40,14 +40,7 @@ export type Source = { | |||
| 	summalyProxy?: string; | ||||
| 
 | ||||
| 	accesslog?: string; | ||||
| 	twitter?: { | ||||
| 		consumer_key: string; | ||||
| 		consumer_secret: string; | ||||
| 	}; | ||||
| 	github?: { | ||||
| 		client_id: string; | ||||
| 		client_secret: string; | ||||
| 	}; | ||||
| 
 | ||||
| 	github_bot?: { | ||||
| 		hook_secret: string; | ||||
| 		username: string; | ||||
|  |  | |||
|  | @ -11,7 +11,9 @@ const defaultMeta: any = { | |||
| 		originalNotesCount: 0, | ||||
| 		originalUsersCount: 0 | ||||
| 	}, | ||||
| 	maxNoteTextLength: 1000 | ||||
| 	maxNoteTextLength: 1000, | ||||
| 	enableTwitterIntegration: false, | ||||
| 	enableGithubIntegration: false, | ||||
| }; | ||||
| 
 | ||||
| export default async function(): Promise<IMeta> { | ||||
|  |  | |||
|  | @ -99,6 +99,32 @@ if ((config as any).maintainer) { | |||
| 		} | ||||
| 	}); | ||||
| } | ||||
| if ((config as any).twitter) { | ||||
| 	Meta.findOne({}).then(m => { | ||||
| 		if (m != null && m.enableTwitterIntegration == null) { | ||||
| 			Meta.update({}, { | ||||
| 				$set: { | ||||
| 					enableTwitterIntegration: true, | ||||
| 					twitterConsumerKey: (config as any).twitter.consumer_key, | ||||
| 					twitterConsumerSecret: (config as any).twitter.consumer_secret | ||||
| 				} | ||||
| 			}); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| if ((config as any).github) { | ||||
| 	Meta.findOne({}).then(m => { | ||||
| 		if (m != null && m.enableGithubIntegration == null) { | ||||
| 			Meta.update({}, { | ||||
| 				$set: { | ||||
| 					enableGithubIntegration: true, | ||||
| 					githubClientId: (config as any).github.client_id, | ||||
| 					githubClientSecret: (config as any).github.client_secret | ||||
| 				} | ||||
| 			}); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| export type IMeta = { | ||||
| 	name?: string; | ||||
|  | @ -157,4 +183,12 @@ export type IMeta = { | |||
| 	 * Max allowed note text length in charactors | ||||
| 	 */ | ||||
| 	maxNoteTextLength?: number; | ||||
| 
 | ||||
| 	enableTwitterIntegration?: boolean; | ||||
| 	twitterConsumerKey?: string; | ||||
| 	twitterConsumerSecret?: string; | ||||
| 
 | ||||
| 	enableGithubIntegration?: boolean; | ||||
| 	githubClientId?: string; | ||||
| 	githubClientSecret?: string; | ||||
| }; | ||||
|  |  | |||
|  | @ -137,7 +137,49 @@ export const meta = { | |||
| 			desc: { | ||||
| 				'ja-JP': 'インスタンスの対象言語' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		enableTwitterIntegration: { | ||||
| 			validator: $.bool.optional, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'Twitter連携機能を有効にするか否か' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		twitterConsumerKey: { | ||||
| 			validator: $.str.optional.nullable, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'TwitterアプリのConsumer key' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		twitterConsumerSecret: { | ||||
| 			validator: $.str.optional.nullable, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'TwitterアプリのConsumer secret' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		enableGithubIntegration: { | ||||
| 			validator: $.bool.optional, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'GitHub連携機能を有効にするか否か' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		githubClientId: { | ||||
| 			validator: $.str.optional.nullable, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'GitHubアプリのClient ID' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		githubClientSecret: { | ||||
| 			validator: $.str.optional.nullable, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'GitHubアプリのClient secret' | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  | @ -216,6 +258,30 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { | |||
| 		set.langs = ps.langs; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.enableTwitterIntegration !== undefined) { | ||||
| 		set.enableTwitterIntegration = ps.enableTwitterIntegration; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.twitterConsumerKey !== undefined) { | ||||
| 		set.twitterConsumerKey = ps.twitterConsumerKey; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.twitterConsumerSecret !== undefined) { | ||||
| 		set.twitterConsumerSecret = ps.twitterConsumerSecret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.enableGithubIntegration !== undefined) { | ||||
| 		set.enableGithubIntegration = ps.enableGithubIntegration; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.githubClientId !== undefined) { | ||||
| 		set.githubClientId = ps.githubClientId; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.githubClientSecret !== undefined) { | ||||
| 		set.githubClientSecret = ps.githubClientSecret; | ||||
| 	} | ||||
| 
 | ||||
| 	await Meta.update({}, { | ||||
| 		$set: set | ||||
| 	}, { upsert: true }); | ||||
|  |  | |||
|  | @ -77,8 +77,8 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { | |||
| 			elasticsearch: config.elasticsearch ? true : false, | ||||
| 			recaptcha: instance.enableRecaptcha, | ||||
| 			objectStorage: config.drive && config.drive.storage === 'minio', | ||||
| 			twitter: config.twitter ? true : false, | ||||
| 			github: config.github ? true : false, | ||||
| 			twitter: instance.enableTwitterIntegration, | ||||
| 			github: instance.enableGithubIntegration, | ||||
| 			serviceWorker: config.sw ? true : false, | ||||
| 			userRecommendation: config.user_recommendation ? config.user_recommendation : {} | ||||
| 		}; | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ router.post('/signup', require('./private/signup').default); | |||
| router.post('/signin', require('./private/signin').default); | ||||
| 
 | ||||
| router.use(require('./service/github').routes()); | ||||
| router.use(require('./service/github-bot').routes()); | ||||
| router.use(require('./service/twitter').routes()); | ||||
| 
 | ||||
| router.use(require('./mastodon').routes()); | ||||
|  |  | |||
							
								
								
									
										156
									
								
								src/server/api/service/github-bot.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								src/server/api/service/github-bot.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,156 @@ | |||
| import * as EventEmitter from 'events'; | ||||
| import * as Router from 'koa-router'; | ||||
| import * as request from 'request'; | ||||
| import User, { IUser } from '../../../models/user'; | ||||
| import createNote from '../../../services/note/create'; | ||||
| import config from '../../../config'; | ||||
| const crypto = require('crypto'); | ||||
| 
 | ||||
| const handler = new EventEmitter(); | ||||
| 
 | ||||
| let bot: IUser; | ||||
| 
 | ||||
| const post = async (text: string, home = true) => { | ||||
| 	if (bot == null) { | ||||
| 		const account = await User.findOne({ | ||||
| 			usernameLower: config.github_bot.username.toLowerCase() | ||||
| 		}); | ||||
| 
 | ||||
| 		if (account == null) { | ||||
| 			console.warn(`GitHub hook bot specified, but not found: @${config.github_bot.username}`); | ||||
| 			return; | ||||
| 		} else { | ||||
| 			bot = account; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	createNote(bot, { text, visibility: home ? 'home' : 'public' }); | ||||
| }; | ||||
| 
 | ||||
| // Init router
 | ||||
| const router = new Router(); | ||||
| 
 | ||||
| if (config.github_bot) { | ||||
| 	const secret = config.github_bot.hook_secret; | ||||
| 
 | ||||
| 	router.post('/hooks/github', ctx => { | ||||
| 		const body = JSON.stringify(ctx.request.body); | ||||
| 		const hash = crypto.createHmac('sha1', secret).update(body).digest('hex'); | ||||
| 		const sig1 = new Buffer(ctx.headers['x-hub-signature']); | ||||
| 		const sig2 = new Buffer(`sha1=${hash}`); | ||||
| 
 | ||||
| 		// シグネチャ比較
 | ||||
| 		if (sig1.equals(sig2)) { | ||||
| 			handler.emit(ctx.headers['x-github-event'], ctx.request.body); | ||||
| 			ctx.status = 204; | ||||
| 		} else { | ||||
| 			ctx.status = 400; | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| module.exports = router; | ||||
| 
 | ||||
| handler.on('status', event => { | ||||
| 	const state = event.state; | ||||
| 	switch (state) { | ||||
| 		case 'error': | ||||
| 		case 'failure': | ||||
| 			const commit = event.commit; | ||||
| 			const parent = commit.parents[0]; | ||||
| 
 | ||||
| 			// Fetch parent status
 | ||||
| 			request({ | ||||
| 				url: `${parent.url}/statuses`, | ||||
| 				proxy: config.proxy, | ||||
| 				headers: { | ||||
| 					'User-Agent': 'misskey' | ||||
| 				} | ||||
| 			}, (err, res, body) => { | ||||
| 				if (err) { | ||||
| 					console.error(err); | ||||
| 					return; | ||||
| 				} | ||||
| 				const parentStatuses = JSON.parse(body); | ||||
| 				const parentState = parentStatuses[0].state; | ||||
| 				const stillFailed = parentState == 'failure' || parentState == 'error'; | ||||
| 				if (stillFailed) { | ||||
| 					post(`**⚠️BUILD STILL FAILED⚠️**: ?[${commit.commit.message}](${commit.html_url})`); | ||||
| 				} else { | ||||
| 					post(`**🚨BUILD FAILED🚨**: →→→?[${commit.commit.message}](${commit.html_url})←←←`); | ||||
| 				} | ||||
| 			}); | ||||
| 			break; | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
| handler.on('push', event => { | ||||
| 	const ref = event.ref; | ||||
| 	switch (ref) { | ||||
| 		case 'refs/heads/master': | ||||
| 			const pusher = event.pusher; | ||||
| 			const compare = event.compare; | ||||
| 			const commits: any[] = event.commits; | ||||
| 			post([ | ||||
| 				`Pushed by **${pusher.name}** with ?[${commits.length} commit${commits.length > 1 ? 's' : ''}](${compare}):`, | ||||
| 				commits.reverse().map(commit => `・[?[${commit.id.substr(0, 7)}](${commit.url})] ${commit.message.split('\n')[0]}`).join('\n'), | ||||
| 			].join('\n')); | ||||
| 			break; | ||||
| 		case 'refs/heads/release': | ||||
| 			const commit = event.commits[0]; | ||||
| 			post(`RELEASED: ${commit.message}`); | ||||
| 			break; | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
| handler.on('issues', event => { | ||||
| 	const issue = event.issue; | ||||
| 	const action = event.action; | ||||
| 	let title: string; | ||||
| 	switch (action) { | ||||
| 		case 'opened': title = 'Issue opened'; break; | ||||
| 		case 'closed': title = 'Issue closed'; break; | ||||
| 		case 'reopened': title = 'Issue reopened'; break; | ||||
| 		default: return; | ||||
| 	} | ||||
| 	post(`${title}: <${issue.number}>「${issue.title}」\n${issue.html_url}`); | ||||
| }); | ||||
| 
 | ||||
| handler.on('issue_comment', event => { | ||||
| 	const issue = event.issue; | ||||
| 	const comment = event.comment; | ||||
| 	const action = event.action; | ||||
| 	let text: string; | ||||
| 	switch (action) { | ||||
| 		case 'created': text = `Commented to「${issue.title}」:${comment.user.login}「${comment.body}」\n${comment.html_url}`; break; | ||||
| 		default: return; | ||||
| 	} | ||||
| 	post(text); | ||||
| }); | ||||
| 
 | ||||
| handler.on('watch', event => { | ||||
| 	const sender = event.sender; | ||||
| 	post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false); | ||||
| }); | ||||
| 
 | ||||
| handler.on('fork', event => { | ||||
| 	const repo = event.forkee; | ||||
| 	post(`🍴 Forked:\n${repo.html_url} 🍴`); | ||||
| }); | ||||
| 
 | ||||
| handler.on('pull_request', event => { | ||||
| 	const pr = event.pull_request; | ||||
| 	const action = event.action; | ||||
| 	let text: string; | ||||
| 	switch (action) { | ||||
| 		case 'opened': text = `New Pull Request:「${pr.title}」\n${pr.html_url}`; break; | ||||
| 		case 'reopened': text = `Pull Request Reopened:「${pr.title}」\n${pr.html_url}`; break; | ||||
| 		case 'closed': | ||||
| 			text = pr.merged | ||||
| 				? `Pull Request Merged!:「${pr.title}」\n${pr.html_url}` | ||||
| 				: `Pull Request Closed:「${pr.title}」\n${pr.html_url}`; | ||||
| 			break; | ||||
| 		default: return; | ||||
| 	} | ||||
| 	post(text); | ||||
| }); | ||||
|  | @ -1,37 +1,14 @@ | |||
| import * as EventEmitter from 'events'; | ||||
| import * as Koa from 'koa'; | ||||
| import * as Router from 'koa-router'; | ||||
| import * as request from 'request'; | ||||
| import { OAuth2 } from 'oauth'; | ||||
| import User, { IUser, pack, ILocalUser } from '../../../models/user'; | ||||
| import createNote from '../../../services/note/create'; | ||||
| import User, { pack, ILocalUser } from '../../../models/user'; | ||||
| import config from '../../../config'; | ||||
| import { publishMainStream } from '../../../stream'; | ||||
| import redis from '../../../db/redis'; | ||||
| import uuid = require('uuid'); | ||||
| import signin from '../common/signin'; | ||||
| const crypto = require('crypto'); | ||||
| 
 | ||||
| const handler = new EventEmitter(); | ||||
| 
 | ||||
| let bot: IUser; | ||||
| 
 | ||||
| const post = async (text: string, home = true) => { | ||||
| 	if (bot == null) { | ||||
| 		const account = await User.findOne({ | ||||
| 			usernameLower: config.github_bot.username.toLowerCase() | ||||
| 		}); | ||||
| 
 | ||||
| 		if (account == null) { | ||||
| 			console.warn(`GitHub hook bot specified, but not found: @${config.github_bot.username}`); | ||||
| 			return; | ||||
| 		} else { | ||||
| 			bot = account; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	createNote(bot, { text, visibility: home ? 'home' : 'public' }); | ||||
| }; | ||||
| import fetchMeta from '../../../misc/fetch-meta'; | ||||
| 
 | ||||
| function getUserToken(ctx: Koa.Context) { | ||||
| 	return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1]; | ||||
|  | @ -80,21 +57,20 @@ router.get('/disconnect/github', async ctx => { | |||
| 	})); | ||||
| }); | ||||
| 
 | ||||
| if (!config.github || !redis) { | ||||
| 	router.get('/connect/github', ctx => { | ||||
| 		ctx.body = '現在GitHubへ接続できません (このインスタンスではGitHubはサポートされていません)'; | ||||
| 	}); | ||||
| async function getOath2() { | ||||
| 	const meta = await fetchMeta(); | ||||
| 
 | ||||
| 	router.get('/signin/github', ctx => { | ||||
| 		ctx.body = '現在GitHubへ接続できません (このインスタンスではGitHubはサポートされていません)'; | ||||
| 	}); | ||||
| } else { | ||||
| 	const oauth2 = new OAuth2( | ||||
| 		config.github.client_id, | ||||
| 		config.github.client_secret, | ||||
| 	if (meta.enableGithubIntegration) { | ||||
| 		return new OAuth2( | ||||
| 			meta.githubClientId, | ||||
| 			meta.githubClientSecret, | ||||
| 			'https://github.com/', | ||||
| 			'login/oauth/authorize', | ||||
| 			'login/oauth/access_token'); | ||||
| 	} else { | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| router.get('/connect/github', async ctx => { | ||||
| 	if (!compareOrigin(ctx)) { | ||||
|  | @ -115,6 +91,8 @@ if (!config.github || !redis) { | |||
| 	}; | ||||
| 
 | ||||
| 	redis.set(userToken, JSON.stringify(params)); | ||||
| 
 | ||||
| 	const oauth2 = await getOath2(); | ||||
| 	ctx.redirect(oauth2.getAuthorizeUrl(params)); | ||||
| }); | ||||
| 
 | ||||
|  | @ -138,12 +116,16 @@ if (!config.github || !redis) { | |||
| 	}); | ||||
| 
 | ||||
| 	redis.set(sessid, JSON.stringify(params)); | ||||
| 
 | ||||
| 	const oauth2 = await getOath2(); | ||||
| 	ctx.redirect(oauth2.getAuthorizeUrl(params)); | ||||
| }); | ||||
| 
 | ||||
| router.get('/gh/cb', async ctx => { | ||||
| 	const userToken = getUserToken(ctx); | ||||
| 
 | ||||
| 	const oauth2 = await getOath2(); | ||||
| 
 | ||||
| 	if (!userToken) { | ||||
| 		const sessid = ctx.cookies.get('signin_with_github_session_id'); | ||||
| 
 | ||||
|  | @ -288,129 +270,5 @@ if (!config.github || !redis) { | |||
| 		})); | ||||
| 	} | ||||
| }); | ||||
| } | ||||
| 
 | ||||
| if (config.github_bot) { | ||||
| 	const secret = config.github_bot.hook_secret; | ||||
| 
 | ||||
| 	router.post('/hooks/github', ctx => { | ||||
| 		const body = JSON.stringify(ctx.request.body); | ||||
| 		const hash = crypto.createHmac('sha1', secret).update(body).digest('hex'); | ||||
| 		const sig1 = new Buffer(ctx.headers['x-hub-signature']); | ||||
| 		const sig2 = new Buffer(`sha1=${hash}`); | ||||
| 
 | ||||
| 		// シグネチャ比較
 | ||||
| 		if (sig1.equals(sig2)) { | ||||
| 			handler.emit(ctx.headers['x-github-event'], ctx.request.body); | ||||
| 			ctx.status = 204; | ||||
| 		} else { | ||||
| 			ctx.status = 400; | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| module.exports = router; | ||||
| 
 | ||||
| handler.on('status', event => { | ||||
| 	const state = event.state; | ||||
| 	switch (state) { | ||||
| 		case 'error': | ||||
| 		case 'failure': | ||||
| 			const commit = event.commit; | ||||
| 			const parent = commit.parents[0]; | ||||
| 
 | ||||
| 			// Fetch parent status
 | ||||
| 			request({ | ||||
| 				url: `${parent.url}/statuses`, | ||||
| 				proxy: config.proxy, | ||||
| 				headers: { | ||||
| 					'User-Agent': 'misskey' | ||||
| 				} | ||||
| 			}, (err, res, body) => { | ||||
| 				if (err) { | ||||
| 					console.error(err); | ||||
| 					return; | ||||
| 				} | ||||
| 				const parentStatuses = JSON.parse(body); | ||||
| 				const parentState = parentStatuses[0].state; | ||||
| 				const stillFailed = parentState == 'failure' || parentState == 'error'; | ||||
| 				if (stillFailed) { | ||||
| 					post(`**⚠️BUILD STILL FAILED⚠️**: ?[${commit.commit.message}](${commit.html_url})`); | ||||
| 				} else { | ||||
| 					post(`**🚨BUILD FAILED🚨**: →→→?[${commit.commit.message}](${commit.html_url})←←←`); | ||||
| 				} | ||||
| 			}); | ||||
| 			break; | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
| handler.on('push', event => { | ||||
| 	const ref = event.ref; | ||||
| 	switch (ref) { | ||||
| 		case 'refs/heads/master': | ||||
| 			const pusher = event.pusher; | ||||
| 			const compare = event.compare; | ||||
| 			const commits: any[] = event.commits; | ||||
| 			post([ | ||||
| 				`Pushed by **${pusher.name}** with ?[${commits.length} commit${commits.length > 1 ? 's' : ''}](${compare}):`, | ||||
| 				commits.reverse().map(commit => `・[?[${commit.id.substr(0, 7)}](${commit.url})] ${commit.message.split('\n')[0]}`).join('\n'), | ||||
| 			].join('\n')); | ||||
| 			break; | ||||
| 		case 'refs/heads/release': | ||||
| 			const commit = event.commits[0]; | ||||
| 			post(`RELEASED: ${commit.message}`); | ||||
| 			break; | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
| handler.on('issues', event => { | ||||
| 	const issue = event.issue; | ||||
| 	const action = event.action; | ||||
| 	let title: string; | ||||
| 	switch (action) { | ||||
| 		case 'opened': title = 'Issue opened'; break; | ||||
| 		case 'closed': title = 'Issue closed'; break; | ||||
| 		case 'reopened': title = 'Issue reopened'; break; | ||||
| 		default: return; | ||||
| 	} | ||||
| 	post(`${title}: <${issue.number}>「${issue.title}」\n${issue.html_url}`); | ||||
| }); | ||||
| 
 | ||||
| handler.on('issue_comment', event => { | ||||
| 	const issue = event.issue; | ||||
| 	const comment = event.comment; | ||||
| 	const action = event.action; | ||||
| 	let text: string; | ||||
| 	switch (action) { | ||||
| 		case 'created': text = `Commented to「${issue.title}」:${comment.user.login}「${comment.body}」\n${comment.html_url}`; break; | ||||
| 		default: return; | ||||
| 	} | ||||
| 	post(text); | ||||
| }); | ||||
| 
 | ||||
| handler.on('watch', event => { | ||||
| 	const sender = event.sender; | ||||
| 	post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false); | ||||
| }); | ||||
| 
 | ||||
| handler.on('fork', event => { | ||||
| 	const repo = event.forkee; | ||||
| 	post(`🍴 Forked:\n${repo.html_url} 🍴`); | ||||
| }); | ||||
| 
 | ||||
| handler.on('pull_request', event => { | ||||
| 	const pr = event.pull_request; | ||||
| 	const action = event.action; | ||||
| 	let text: string; | ||||
| 	switch (action) { | ||||
| 		case 'opened': text = `New Pull Request:「${pr.title}」\n${pr.html_url}`; break; | ||||
| 		case 'reopened': text = `Pull Request Reopened:「${pr.title}」\n${pr.html_url}`; break; | ||||
| 		case 'closed': | ||||
| 			text = pr.merged | ||||
| 				? `Pull Request Merged!:「${pr.title}」\n${pr.html_url}` | ||||
| 				: `Pull Request Closed:「${pr.title}」\n${pr.html_url}`; | ||||
| 			break; | ||||
| 		default: return; | ||||
| 	} | ||||
| 	post(text); | ||||
| }); | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import User, { pack, ILocalUser } from '../../../models/user'; | |||
| import { publishMainStream } from '../../../stream'; | ||||
| import config from '../../../config'; | ||||
| import signin from '../common/signin'; | ||||
| import fetchMeta from '../../../misc/fetch-meta'; | ||||
| 
 | ||||
| function getUserToken(ctx: Koa.Context) { | ||||
| 	return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1]; | ||||
|  | @ -55,20 +56,19 @@ router.get('/disconnect/twitter', async ctx => { | |||
| 	})); | ||||
| }); | ||||
| 
 | ||||
| if (config.twitter == null || redis == null) { | ||||
| 	router.get('/connect/twitter', ctx => { | ||||
| 		ctx.body = '現在Twitterへ接続できません (このインスタンスではTwitterはサポートされていません)'; | ||||
| 	}); | ||||
| async function getTwAuth() { | ||||
| 	const meta = await fetchMeta(); | ||||
| 
 | ||||
| 	router.get('/signin/twitter', ctx => { | ||||
| 		ctx.body = '現在Twitterへ接続できません (このインスタンスではTwitterはサポートされていません)'; | ||||
| 	}); | ||||
| } else { | ||||
| 	const twAuth = autwh({ | ||||
| 		consumerKey: config.twitter.consumer_key, | ||||
| 		consumerSecret: config.twitter.consumer_secret, | ||||
| 	if (meta.enableTwitterIntegration) { | ||||
| 		return autwh({ | ||||
| 			consumerKey: meta.twitterConsumerKey, | ||||
| 			consumerSecret: meta.twitterConsumerSecret, | ||||
| 			callbackUrl: `${config.url}/api/tw/cb` | ||||
| 		}); | ||||
| 	} else { | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| router.get('/connect/twitter', async ctx => { | ||||
| 	if (!compareOrigin(ctx)) { | ||||
|  | @ -82,12 +82,14 @@ if (config.twitter == null || redis == null) { | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const twAuth = await getTwAuth(); | ||||
| 	const twCtx = await twAuth.begin(); | ||||
| 	redis.set(userToken, JSON.stringify(twCtx)); | ||||
| 	ctx.redirect(twCtx.url); | ||||
| }); | ||||
| 
 | ||||
| router.get('/signin/twitter', async ctx => { | ||||
| 	const twAuth = await getTwAuth(); | ||||
| 	const twCtx = await twAuth.begin(); | ||||
| 
 | ||||
| 	const sessid = uuid(); | ||||
|  | @ -110,6 +112,8 @@ if (config.twitter == null || redis == null) { | |||
| router.get('/tw/cb', async ctx => { | ||||
| 	const userToken = getUserToken(ctx); | ||||
| 
 | ||||
| 	const twAuth = await getTwAuth(); | ||||
| 
 | ||||
| 	if (userToken == null) { | ||||
| 		const sessid = ctx.cookies.get('signin_with_twitter_session_id'); | ||||
| 
 | ||||
|  | @ -180,6 +184,5 @@ if (config.twitter == null || redis == null) { | |||
| 		})); | ||||
| 	} | ||||
| }); | ||||
| } | ||||
| 
 | ||||
| module.exports = router; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue