summaryrefslogtreecommitdiff
path: root/src/server/api/service/github-bot.ts
blob: cb038363f3a7c94e18f629ac0b6cd7cdad0b7b8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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);
});