Last active
December 22, 2015 17:28
-
-
Save syoichi/6506075 to your computer and use it in GitHub Desktop.
Tombfixの新しいTwitter Modelの実装のWIP Patch。
https://github.com/tombfix/core/issues/60
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Models.register(update({ | |
name : 'Twitter Mod', | |
ICON : 'https://twitter.com/favicon.ico', | |
ORIGIN : 'https://twitter.com', | |
ACCOUT_URL : 'https://twitter.com/settings/account', | |
TWEET_API_URL : 'https://twitter.com/i/tweet', | |
UPLOAD_API_URL : 'https://upload.twitter.com/i/tweet/create_with_media.iframe', | |
STATUS_MAX_LENGTH : 140, | |
SHORT_URL_LENGTH : 22, | |
check : function (ps) { | |
return /^(?:regular|photo|quote|link|conversation|video)$/.test(ps.type); | |
}, | |
post : function (ps) { | |
var status = this.createStatus(ps); | |
return this.getToken().addCallback(token => { | |
if (ps.type === 'photo') { | |
return this.upload(ps, token, status); | |
} | |
return this.update(token, status); | |
}); | |
}, | |
getToken : function () { | |
return request(this.ACCOUT_URL, { | |
responseType : 'document' | |
}).addCallback(({response : doc}) => { | |
if (doc.body.classList.contains('logged-out')) { | |
throw new Error(getMessage('error.notLoggedin')); | |
} | |
return { | |
authenticity_token : doc.querySelector('.authenticity_token').value | |
}; | |
}); | |
}, | |
createStatus : function (ps) { | |
var contents, maxLen, status, statusLen, over; | |
contents = { | |
desc : (ps.description || '').trim(), | |
quote : ps.type !== 'video' && ps.body ? ps.body.trim().wrap('"') : '', | |
title : ps.item.trim(), | |
url : ps.itemUrl || '', | |
tags : (ps.tags || []).map(tag => '#' + tag) | |
}; | |
maxLen = this.STATUS_MAX_LENGTH; | |
if (ps.type === 'photo') { | |
contents.url = ps.pageUrl; | |
maxLen -= this.SHORT_URL_LENGTH + 1; | |
} | |
status = this.joinContents(contents); | |
if (contents.usage && !contents.usage.url) { | |
contents.url = ''; | |
} | |
if (contents.url) { | |
let padding = /^https:\/\//.test(contents.url) ? 2 : 1, | |
tmp = update({}, contents); | |
tmp.url = ''; | |
statusLen = this.joinContents(tmp).charLength + (this.SHORT_URL_LENGTH + padding); | |
} else { | |
statusLen = status.charLength; | |
} | |
over = statusLen - maxLen; | |
if (over > 0/* && getPref('model.twitter.truncateStatus')*/) { | |
return this.truncateStatus(contents, over); | |
} | |
return status; | |
}, | |
joinContents : function ({desc, quote, title, url, tags}) { | |
var prefix = desc ? '' : '見てる:'; // getPref('model.twitter.template.prefix') 例: 見てる: | |
// getPref('model.twitter.template') | |
if (true) { | |
return this.extractTemplate(prefix, arguments[0]); | |
} | |
return joinText([prefix, desc, quote, title, url, ...tags], ' '); | |
}, | |
extractTemplate : function (prefix, contents) { | |
var template = '%desc% %quote% %title% %url%%br%%tags% [via Test]'; // getPref('model.twitter.template') 例: %desc% %quote% %title% %url%%br%%tags% [via Test] | |
contents.usage = {}; | |
template = template.replace(/%(desc|quote|title|url|tags|br)%/g, (match, name) => { | |
if (name === 'br') { | |
return '\n'; | |
} | |
contents.usage[name] = true; | |
if (contents[name].length) { | |
return match; | |
} | |
return ''; | |
}).replace(/^ +| +$/mg, '').replace(/ +/g, ' '); | |
return joinText([prefix, ...(template.split(' '))].map(content => { | |
return content.replace(/%(desc|quote|title|url|tags)%/g, (match, name) => { | |
if (name === 'tags') { | |
return joinText([...tags], ' '); | |
} | |
return contents[name]; | |
}); | |
}), ' '); | |
}, | |
truncateStatus : function (contents, over) { | |
var truncator = { | |
tags : tags => { | |
contents.tags = tags = tags.reverse().filter(tag => { | |
if (over <= 0) { | |
return true; | |
} | |
over -= tag.charLength + 1; | |
}).reverse(); | |
if (tags.length || over <= 0) { | |
return true; | |
} | |
}, | |
title : title => { | |
var len = contents.title.charLength; | |
contents.title = title = title.slice(0, -(over + 1)); | |
if (title.charLength) { | |
contents.title += '…'; | |
} else { | |
over -= len + 1; | |
if (over > 0) { | |
return false; | |
} | |
} | |
return true; | |
}, | |
quote : quote => { | |
quote = quote.slice(1, -(over + 2)); | |
if (quote.charLength) { | |
contents.quote = (quote + '…').wrap('"'); | |
} else { | |
over -= contents.quote.charLength + 1; | |
contents.quote = quote; | |
if (over > 0) { | |
return false; | |
} | |
} | |
return true; | |
}, | |
desc : desc => { | |
contents.desc = desc.slice(0, -(over + 1)) + '…'; | |
} | |
}; | |
for (let name of Object.keys(truncator)) { | |
if (contents.usage && !contents.usage[name]) { | |
contents[name] = name === 'tags' ? [] : ''; | |
} | |
let content = contents[name]; | |
if (content.length && truncator[name](content)) { | |
break; | |
} | |
} | |
return this.joinContents(contents); | |
}, | |
update : function (token, status) { | |
token.status = status; | |
return request(this.TWEET_API_URL + '/create', { | |
sendContent : token, | |
}).addErrback(err => { | |
throw new Error(JSON.parse(err.message.responseText).message.trimTag()); | |
}); | |
}, | |
upload : function (ps, token, status) { | |
return (ps.file ? succeed(ps.file) : download(ps.itemUrl, getTempDir())).addCallback(file => { | |
var bis = new BinaryInputStream(new FileInputStream(file, -1, 0, false)); | |
return request(this.UPLOAD_API_URL, { | |
sendContent : { | |
status : status, | |
'media_data[]' : btoa(bis.readBytes(bis.available())), | |
iframe_callback : 'window.top.swift_tweetbox_tombfix', | |
post_authenticity_token : token.authenticity_token | |
} | |
}).addErrback(({message : msg}) => { | |
if (msg.responseText.length) { | |
let json = msg.responseText.extract( | |
/window.top.swift_tweetbox_tombfix\((\{.+\})\);/ | |
); | |
throw new Error(JSON.parse(json).error); | |
} | |
// image is over 3MB | |
throw new Error(msg.message || 'The file you selected is greater than the 3MB limit.'); // getMessage('message.model.twitter.upload') 選択した画像は 3MB を超えています。 | |
}); | |
}); | |
}, | |
favor : function (ps) { | |
return this.getToken().addCallback(token => { | |
token.id = ps.favorite.id; | |
return request(this.TWEET_API_URL + '/favorite', { | |
sendContent : token | |
}); | |
}); | |
}, | |
login : function (user, password) { | |
notify(this.name, getMessage('message.changeAccount.logout'), this.ICON); | |
return (this.getCurrentUser() ? this.logout() : succeed()).addCallback(() => { | |
return request(this.ORIGIN, { | |
responseType : 'document' | |
}).addCallback(({response : doc}) => { | |
var form = doc.querySelector('form.signin'); | |
notify(this.name, getMessage('message.changeAccount.login'), this.ICON); | |
return request(this.ORIGIN + '/sessions', { | |
sendContent : update(formContents(form), { | |
'session[username_or_email]' : user, | |
'session[password]' : password | |
}) | |
}); | |
}); | |
}).addCallback(() => { | |
this.updateSession(); | |
this.user = user; | |
notify(this.name, getMessage('message.changeAccount.done'), this.ICON); | |
}); | |
}, | |
logout : function () { | |
return request(this.ACCOUT_URL, { | |
responseType : 'document' | |
}).addCallback(({response : doc}) => { | |
return request(this.ORIGIN + '/logout', { | |
sendContent : formContents(doc.getElementById('signout-form')) | |
}); | |
}); | |
}, | |
getAuthCookie : function () { | |
return getCookieString('twitter.com', 'auth_token'); | |
}, | |
getCurrentUser : function () { | |
return request(this.ACCOUT_URL, { | |
responseType : 'document' | |
}).addCallback(({response : doc}) => { | |
var user = doc.getElementsByName('user[screen_name]')[0].value; | |
if (!/[^\w]/.test(user)) { | |
this.user = user; | |
} | |
return user; | |
}); | |
}, | |
getPasswords : function () { | |
return getPasswords(this.ORIGIN); | |
} | |
}, AbstractSessionService)); | |
Object.defineProperties(String.prototype, { | |
'charLength' : { | |
set : function () { | |
return this.charLength; | |
}, | |
get : function () { | |
return this.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '1').length; | |
}, | |
enumerable : false, | |
configurable : true | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment