Skip to content
This repository was archived by the owner on Jul 17, 2020. It is now read-only.

Commit 4bd0116

Browse files
author
Zirak
committed
Auto-detect unformatted code blocks.
Fixes #238. When a user posts a messages which looks like a code block, we wait a few seconds, and if it wasn't fixed, ping the user telling him to format his message. Will also bin the message if the user has <2k rep.
1 parent 4a7eade commit 4bd0116

File tree

4 files changed

+261
-11
lines changed

4 files changed

+261
-11
lines changed

master.js

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2587,6 +2587,19 @@ bot.adapter = {
25872587
text : this.escape( text ),
25882588
url : url
25892589
});
2590+
},
2591+
2592+
moveMessage : function ( msgid, fromRoom, toRoom, cb ) {
2593+
IO.xhr({
2594+
method : 'POST',
2595+
url : '/admin/movePosts/' + fromRoom,
2596+
data : {
2597+
fkey: bot.adapter.fkey,
2598+
to: toRoom,
2599+
ids: msgid
2600+
},
2601+
finish : cb || function () {}
2602+
});
25902603
}
25912604
};
25922605

@@ -2741,17 +2754,17 @@ var polling = bot.adapter.in = {
27412754
},
27422755

27432756
handleMultilineMessage : function ( msg ) {
2744-
this.breakMultilineMessage( msg ).forEach(function ( line ) {
2757+
this.breakMultilineMessage( msg.content ).forEach(function ( line ) {
27452758
var msgObj = Object.merge( msg, { content : line.trim() });
27462759

27472760
IO.in.receive( msgObj );
27482761
});
27492762
},
2750-
breakMultilineMessage : function ( msg ) {
2763+
breakMultilineMessage : function ( content ) {
27512764
//remove the enclosing tag
2752-
var multiline = msg.content
2765+
var multiline = content
27532766
//slice upto the beginning of the ending tag
2754-
.slice( 0, msg.content.lastIndexOf('</div>') )
2767+
.slice( 0, content.lastIndexOf('</div>') )
27552768
//and strip away the beginning tag
27562769
.replace( '<div class=\'full\'>', '' );
27572770

@@ -6135,6 +6148,119 @@ bot.addCommand({
61356148

61366149
}());
61376150

6151+
;
6152+
(function () {
6153+
6154+
//only activate for SO room 17; TODO consider changing if well accepted
6155+
if (bot.adapter.site !== 'stackoverflow' || bot.adapter.roomid !== 17) {
6156+
bot.log('Not activating unformatted code checking; not in right room/site');
6157+
return;
6158+
}
6159+
6160+
var badMessages = new Map();
6161+
6162+
IO.register( 'rawinput', function checkUnformattedCode (msgObj) {
6163+
var msgid = msgObj.message_id;
6164+
6165+
//only handle new messages and edits
6166+
if (msgObj.event_type !== 1 && msgObj.event_type !== 2) {
6167+
return;
6168+
}
6169+
6170+
//so far it should only apply to the js room
6171+
if ( msgObj.room_id !== 17 ) {
6172+
return;
6173+
}
6174+
6175+
//don't bother with owners
6176+
if ( bot.isOwner(msgObj.user_id) ) {
6177+
return;
6178+
}
6179+
6180+
//and only look at multiline messages
6181+
if ( !msgObj.content.startsWith('<div class=\'full\'>') ) {
6182+
potentiallyUnlecture();
6183+
return;
6184+
}
6185+
6186+
var content = bot.adapter.in.breakMultilineMessage( msgObj.content )
6187+
.map(function (line) {
6188+
//for some reason, chat adds a space prefix for every line...
6189+
return line.replace(/^ /, '');
6190+
}).join( '\n' );
6191+
content = IO.decodehtmlEntities(content);
6192+
6193+
//and messages which aren't code blocks
6194+
if ( content.startsWith('<pre class=\'full\'>') ) {
6195+
potentiallyUnlecture();
6196+
return;
6197+
}
6198+
6199+
var isANaughtyMessage = hasUnformattedCode( content );
6200+
6201+
if ( !isANaughtyMessage ) {
6202+
potentiallyUnlecture();
6203+
return;
6204+
}
6205+
6206+
bot.log( '[formatting] Message {0} is a naughty one'.supplant(msgid) );
6207+
var lectureTimeout = setTimeout( lectureUser, 10000, msgObj, content );
6208+
badMessages.set( msgid, lectureTimeout );
6209+
6210+
function potentiallyUnlecture () {
6211+
if ( badMessages.has(msgid) ) {
6212+
bot.log( '[formatting] Message {0} was fixed'.supplant(msgid) );
6213+
clearTimeout( badMessages.get(msgid) );
6214+
badMessages.delete( msgid );
6215+
}
6216+
}
6217+
});
6218+
6219+
function lectureUser ( msgObj, content ) {
6220+
var user = bot.users[ msgObj.user_id ],
6221+
msgid = msgObj.message_id;
6222+
6223+
bot.log( '[formatting] Lecturing user ' + msgObj.user_name );
6224+
bot.adapter.out.add(
6225+
bot.adapter.reply( msgObj.user_name ) + ' ' + createLecture( content )
6226+
);
6227+
6228+
if ( user && user.reputation < 2000 ) {
6229+
bot.log( '[formatting] Binning offending message' );
6230+
bot.adapter.moveMessage( msgid, msgObj.room_id, 23262 );
6231+
}
6232+
}
6233+
6234+
function createLecture ( content ) {
6235+
var lineCount = content.split('\n').length;
6236+
6237+
var lecture = (
6238+
'Please don\'t post unformatted code - ' +
6239+
'hit Ctrl+K before sending, and see the {0}.'
6240+
).supplant( bot.adapter.link('faq', '/faq') );
6241+
6242+
if ( lineCount >= 10 ) {
6243+
lecture += ' For posting large code blocks, use a paste site like ' +
6244+
'https://gist.github.com, http://hastebin.com or http://pastie.org';
6245+
}
6246+
6247+
return lecture;
6248+
}
6249+
6250+
function hasUnformattedCode ( text ) {
6251+
var lines = text.split( '\n' );
6252+
if ( lines.length < 4 ) {
6253+
return false;
6254+
}
6255+
6256+
var codeyLine = /^\}|^<\//;
6257+
return lines.some( / /.test.bind(codeyLine) );
6258+
}
6259+
6260+
})();
6261+
6262+
;
6263+
61386264
;
61396265
(function () {
61406266
"use strict";

master.min.js

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/adapter.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,19 @@ bot.adapter = {
212212
text : this.escape( text ),
213213
url : url
214214
});
215+
},
216+
217+
moveMessage : function ( msgid, fromRoom, toRoom, cb ) {
218+
IO.xhr({
219+
method : 'POST',
220+
url : '/admin/movePosts/' + fromRoom,
221+
data : {
222+
fkey: bot.adapter.fkey,
223+
to: toRoom,
224+
ids: msgid
225+
},
226+
finish : cb || function () {}
227+
});
215228
}
216229
};
217230

@@ -366,17 +379,17 @@ var polling = bot.adapter.in = {
366379
},
367380

368381
handleMultilineMessage : function ( msg ) {
369-
this.breakMultilineMessage( msg ).forEach(function ( line ) {
382+
this.breakMultilineMessage( msg.content ).forEach(function ( line ) {
370383
var msgObj = Object.merge( msg, { content : line.trim() });
371384

372385
IO.in.receive( msgObj );
373386
});
374387
},
375-
breakMultilineMessage : function ( msg ) {
388+
breakMultilineMessage : function ( content ) {
376389
//remove the enclosing tag
377-
var multiline = msg.content
390+
var multiline = content
378391
//slice upto the beginning of the ending tag
379-
.slice( 0, msg.content.lastIndexOf('</div>') )
392+
.slice( 0, content.lastIndexOf('</div>') )
380393
//and strip away the beginning tag
381394
.replace( '<div class=\'full\'>', '' );
382395

source/plugins/unformatted-code.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
(function () {
2+
3+
//only activate for SO room 17; TODO consider changing if well accepted
4+
if (bot.adapter.site !== 'stackoverflow' || bot.adapter.roomid !== 17) {
5+
bot.log('Not activating unformatted code checking; not in right room/site');
6+
return;
7+
}
8+
9+
var badMessages = new Map();
10+
11+
IO.register( 'rawinput', function checkUnformattedCode (msgObj) {
12+
var msgid = msgObj.message_id;
13+
14+
//only handle new messages and edits
15+
if (msgObj.event_type !== 1 && msgObj.event_type !== 2) {
16+
return;
17+
}
18+
19+
//so far it should only apply to the js room
20+
if ( msgObj.room_id !== 17 ) {
21+
return;
22+
}
23+
24+
//don't bother with owners
25+
if ( bot.isOwner(msgObj.user_id) ) {
26+
return;
27+
}
28+
29+
//and only look at multiline messages
30+
if ( !msgObj.content.startsWith('<div class=\'full\'>') ) {
31+
potentiallyUnlecture();
32+
return;
33+
}
34+
35+
var content = bot.adapter.in.breakMultilineMessage( msgObj.content )
36+
.map(function (line) {
37+
//for some reason, chat adds a space prefix for every line...
38+
return line.replace(/^ /, '');
39+
}).join( '\n' );
40+
content = IO.decodehtmlEntities(content);
41+
42+
//and messages which aren't code blocks
43+
if ( content.startsWith('<pre class=\'full\'>') ) {
44+
potentiallyUnlecture();
45+
return;
46+
}
47+
48+
var isANaughtyMessage = hasUnformattedCode( content );
49+
50+
if ( !isANaughtyMessage ) {
51+
potentiallyUnlecture();
52+
return;
53+
}
54+
55+
bot.log( '[formatting] Message {0} is a naughty one'.supplant(msgid) );
56+
var lectureTimeout = setTimeout( lectureUser, 10000, msgObj, content );
57+
badMessages.set( msgid, lectureTimeout );
58+
59+
function potentiallyUnlecture () {
60+
if ( badMessages.has(msgid) ) {
61+
bot.log( '[formatting] Message {0} was fixed'.supplant(msgid) );
62+
clearTimeout( badMessages.get(msgid) );
63+
badMessages.delete( msgid );
64+
}
65+
}
66+
});
67+
68+
function lectureUser ( msgObj, content ) {
69+
var user = bot.users[ msgObj.user_id ],
70+
msgid = msgObj.message_id;
71+
72+
bot.log( '[formatting] Lecturing user ' + msgObj.user_name );
73+
bot.adapter.out.add(
74+
bot.adapter.reply( msgObj.user_name ) + ' ' + createLecture( content )
75+
);
76+
77+
if ( user && user.reputation < 2000 ) {
78+
bot.log( '[formatting] Binning offending message' );
79+
bot.adapter.moveMessage( msgid, msgObj.room_id, 23262 );
80+
}
81+
}
82+
83+
function createLecture ( content ) {
84+
var lineCount = content.split('\n').length;
85+
86+
var lecture = (
87+
'Please don\'t post unformatted code - ' +
88+
'hit Ctrl+K before sending, use up-arrow to edit messages, ' +
89+
'and see the {0}.'
90+
).supplant( bot.adapter.link('faq', '/faq') );
91+
92+
if ( lineCount >= 10 ) {
93+
lecture += ' For posting large code blocks, use a paste site like ' +
94+
'https://gist.github.com, http://hastebin.com or http://pastie.org';
95+
}
96+
97+
return lecture;
98+
}
99+
100+
function hasUnformattedCode ( text ) {
101+
var lines = text.split( '\n' );
102+
if ( lines.length < 4 ) {
103+
return false;
104+
}
105+
106+
var codeyLine = /^\}|^<\//;
107+
return lines.some( / /.test.bind(codeyLine) );
108+
}
109+
110+
})();

0 commit comments

Comments
 (0)