Skip to content

Commit 4f021ce

Browse files
committed
Add basic functionality
1 parent 3ca3868 commit 4f021ce

File tree

2 files changed

+245
-0
lines changed

2 files changed

+245
-0
lines changed

app.js

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
var request = require('request').defaults({ jar: true });
2+
var WebSocket = require('ws');
3+
4+
try {
5+
var config = require('./config');
6+
}
7+
catch (e) {
8+
console.error('Please set up the configuration file `config.js` first.')
9+
process.exit(1);
10+
}
11+
12+
/**
13+
* Log into StackOverflow with the StackExchange OpenID provider.
14+
*
15+
* @param {String} emailAddress The email address to log in with.
16+
* @param {String} password The password to log in with.
17+
* @param {Function} callback Called on login.
18+
*/
19+
function openIdLogin (emailAddress, password, callback) {
20+
request.get('https://stackoverflow.com/users/login', function (err, resp, body) {
21+
if (err) {
22+
return callback(err);
23+
}
24+
25+
var authRequest = {
26+
uri: 'https://stackoverflow.com/users/authenticate',
27+
followAllRedirects: true,
28+
form: {
29+
openid_identifier: 'https://openid.stackexchange.com',
30+
openid_username: '',
31+
oauth_version: '',
32+
oauth_server: '',
33+
fkey: body.match(/"fkey":"([a-f0-9]{32})"/)[1]
34+
}
35+
};
36+
request.post(authRequest, function (err, resp, body) {
37+
if (err) {
38+
return callback(err);
39+
}
40+
41+
var loginRequest = {
42+
uri: 'https://openid.stackexchange.com/account/login/submit',
43+
followAllRedirects: true,
44+
form: {
45+
email: emailAddress,
46+
password: password,
47+
fkey: body.match(/name="fkey" value="([^"]+)"/)[1],
48+
session: body.match(/name="session" value="([^"]+)"/)[1]
49+
}
50+
};
51+
request.post(loginRequest, function (err, resp, body) {
52+
callback(err);
53+
});
54+
});
55+
});
56+
}
57+
58+
/**
59+
* Connect to chat, receiving the chat `fkey`.
60+
*
61+
* @param {Function} callback Called with the `fkey` as second parameter.
62+
*/
63+
function connectChat (callback) {
64+
request.get('http://chat.stackoverflow.com/', function (err, resp, body) {
65+
if (err) {
66+
return callback(err);
67+
}
68+
69+
var fkey = body.match(/name="fkey"[^>]+value="([a-z0-9]{32})"/)[1];
70+
callback(null, fkey);
71+
});
72+
}
73+
74+
/**
75+
* Join a chat room.
76+
*
77+
* @param {Number} roomId Room identifier.
78+
* @param {String} fkey The chat `fkey`.
79+
* @param {Function} callback Called when the room was joined; the second
80+
* parameter is the websocket address.
81+
*/
82+
function joinRoom (roomId, fkey, callback) {
83+
var chatAuthRequest = {
84+
uri: 'http://chat.stackoverflow.com/ws-auth',
85+
form: {
86+
roomid: roomId,
87+
fkey: fkey
88+
}
89+
};
90+
request.post(chatAuthRequest, function (err, resp, body) {
91+
if (err) {
92+
callback(err);
93+
}
94+
else {
95+
callback(null, JSON.parse(body).url);
96+
}
97+
});
98+
}
99+
100+
/**
101+
* Leave all chat rooms.
102+
*
103+
* @param {String} fkey The chat `fkey`.
104+
* @param {Function} callback Called when done.
105+
*/
106+
function leaveAll (fkey, callback) {
107+
var leaveRequest = {
108+
uri: 'http://chat.stackoverflow.com/chats/leave/all',
109+
form: {
110+
quiet: true,
111+
fkey: fkey
112+
}
113+
}
114+
request.post(leaveRequest, function (err, resp, body) {
115+
callback();
116+
});
117+
}
118+
119+
/**
120+
* Handle a chat API response.
121+
*
122+
* @param {Function} callback Called with the response data.
123+
* @return {Funciton} Response handler.
124+
*/
125+
function handleApiResponse (callback) {
126+
return function (err, resp, body) {
127+
if (callback) {
128+
if (err) {
129+
callback(err);
130+
}
131+
else if (resp.responseCode != 200) {
132+
callback(body);
133+
}
134+
else {
135+
callback(null, JSON.parse(body));
136+
}
137+
}
138+
};
139+
}
140+
141+
142+
/**
143+
* Send chat message.
144+
*
145+
* @param {String} text The message text to send.
146+
* @param {Number} roomId The room identifier.
147+
* @param {String} fkey The chat `fkey`.
148+
* @param {Function} callback Called with the response data.
149+
*/
150+
function sendMessage (text, roomId, fkey, callback) {
151+
var apiRequest = {
152+
uri: 'http://chat.stackoverflow.com/chats/' + roomId + '/messages/new',
153+
form: {
154+
text: text,
155+
fkey: fkey
156+
}
157+
};
158+
request.post(apiRequest, handleApiResponse(callback));
159+
}
160+
161+
/**
162+
* Edit an existing chat message.
163+
*
164+
* @param {String} text The new message text.
165+
* @param {Number} messageId The message identifier.
166+
* @param {String} fkey The chat `fkey`.
167+
* @param {Function} callback Called with the response data.
168+
*/
169+
function editMessage (text, messageId, fkey, callback) {
170+
var apiRequest = {
171+
uri: 'http://chat.stackoverflow.com/messages/' + messageId,
172+
form: {
173+
text: text,
174+
fkey: fkey
175+
}
176+
};
177+
request.post(apiRequest, handleApiResponse(callback));
178+
}
179+
180+
181+
// Example handler
182+
function handleMessageEvent (e, fkey) {
183+
if (e.event_type != 1) {
184+
return;
185+
}
186+
187+
if (e.content.indexOf('!!rabbit') > -1) {
188+
sendMessage('I like rabbits!', e.room_id, fkey, function (err, data) {
189+
console.log(data.id);
190+
});
191+
}
192+
}
193+
194+
195+
// Start
196+
openIdLogin(config.emailAddress, config.password, function (err) {
197+
if (err) {
198+
return console.log(err);
199+
}
200+
201+
console.log('Logged in; starting chat.');
202+
connectChat(function (err, fkey) {
203+
// register SIGINT handler
204+
process.on('SIGINT', function() {
205+
console.log('Shutting down.');
206+
leaveAll(fkey, function () {
207+
process.exit();
208+
});
209+
});
210+
211+
// join room
212+
joinRoom(config.roomId, fkey, function (err, wsAddress) {
213+
var ws = new WebSocket(wsAddress + '?l=0', { origin: 'http://chat.stackoverflow.com' });
214+
ws.on('error', function (err) {
215+
console.log(err);
216+
});
217+
ws.on('open', function () {
218+
console.log('Websocket opened.');
219+
});
220+
ws.on('message', function (message, flags) {
221+
var data = JSON.parse(message);
222+
var room = data.r6;
223+
224+
if (room.e && room.t != room.d) {
225+
room.e.forEach (function (e) {
226+
if (e.event_type == 1) {
227+
handleMessageEvent(e, fkey);
228+
}
229+
else {
230+
console.log(e);
231+
}
232+
});
233+
}
234+
});
235+
});
236+
});
237+
});

config.example.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
// Login data
3+
emailAddress: '',
4+
password: '',
5+
6+
// Room
7+
roomId: 6
8+
};

0 commit comments

Comments
 (0)