From c4b675f45ac58147c9dfa2e31a87d1e8d29bca1a Mon Sep 17 00:00:00 2001 From: liberliber <53687918+liberliber@users.noreply.github.com> Date: Wed, 21 Aug 2019 15:27:23 +0700 Subject: [PATCH] Add files via upload --- src/api.js | 467 +++++++++++++++++++++++++++++++++++++++++++++++++ src/command.js | 80 +++++++++ src/main.js | 84 +++++++++ 3 files changed, 631 insertions(+) create mode 100644 src/api.js create mode 100644 src/command.js create mode 100644 src/main.js diff --git a/src/api.js b/src/api.js new file mode 100644 index 0000000..17835d9 --- /dev/null +++ b/src/api.js @@ -0,0 +1,467 @@ +const thrift = require('thrift-http'); +const unirest = require('unirest'); +const qrcode = require('qrcode-terminal'); +const util = require("util"); +const mime = require("mime"); +const fs = require('fs'); +const path = require('path'); +const rp = require('request-promise'); +const request = require('request'); + +const LineService = require('../curve-thrift/LineService'); +const { + LoginResultType, + IdentityProvider, + ContentType, + Message, + LoginRequest +} = require('../curve-thrift/line_types'); + +const PinVerifier = require('./pinVerifier'); +var config = require('./config'); +var moment = require('moment'); +var reqx = new LoginRequest(); +var reqxy = new LoginRequest(); + +class LineAPI { + constructor() { + this.config = config; + this.setTHttpClient(); + this.axz = false; + this.axy = false; + this.gdLine = "http://gd2.line.naver.jp"; + this.gdLine2 = "http://gf.line.naver.jp"; + } + + setTHttpClient(options = { + protocol: thrift.TCompactProtocol, + transport: thrift.TBufferedTransport, + headers: this.config.Headers, + path: this.config.LINE_HTTP_URL, + https: true + }) { + //options.headers['X-Line-Application'] = 'CHROMEOS\t2.1.0\tChrome_OS\t1'; + options.headers['X-Line-Application'] = 'IOSIPAD 7.14.0 iPhone OS 10.12.0'; + //options.headers['X-Line-Application'] = 'DESKTOPMAC\t5.3.3-YOSEMITE-x64\tMAC\t10.12.0'; + this.options = options; + this.connection = + thrift.createHttpConnection(this.config.LINE_DOMAIN_3RD, 443, this.options); + this.connection.on('error', (err) => { + console.log('err',err); + return err; + }); + if(this.axz === true){ + this._channel = thrift.createHttpClient(require('../curve-thrift/ChannelService.js'), this.connection);this.axz = false; + } else if(this.axy === true){ + this._authService = thrift.createHttpClient(require('../curve-thrift/AuthService.js'), this.connection);this.axy = false; + } else { + this._client = thrift.createHttpClient(LineService, this.connection); + } + } + + async _chanConn(){ + this.options.headers['X-Line-Access'] = this.config.tokenn; + this.options.path = this.config.LINE_CHANNEL_PATH; + this.axz = true; + this.setTHttpClient(this.options); + return Promise.resolve(); + } + + async _authConn(){ + this.axy = true; + this.options.path = this.config.LINE_RS; + this.setTHttpClient(this.options); + return Promise.resolve(); + } + + async _tokenLogin(authToken, certificate) { + this.options.path = this.config.LINE_COMMAND_PATH; + this.config.Headers['X-Line-Access'] = authToken;config.tokenn = authToken; + this.setTHttpClient(this.options); + return Promise.resolve({ authToken, certificate }); + } + + _qrCodeLogin() { + this.setTHttpClient(); + return new Promise((resolve, reject) => { + this._client.getAuthQrcode(true, 'N0HE4D-YOUNG',(err, result) => { + const qrcodeUrl = `line://au/q/${result.verifier}`; + qrcode.generate(qrcodeUrl,{small: true}); + console.info(`\n\nlink qr code is: ${qrcodeUrl}`) + Object.assign(this.config.Headers,{ 'X-Line-Access': result.verifier }); + unirest.get('https://gd2.line.naver.jp/Q') + .headers(this.config.Headers) + .timeout(120000) + .end(async (res) => { + const verifiedQr = res.body.result.verifier; + this._authConn(); + reqx.type = 1; + reqx.verifier = verifiedQr; + this._authService.loginZ(reqx,(err,success) => { + config.tokenn = success.authToken; + config.certificate = success.certificate; + const authToken = config.tokenn; + const certificate = config.certificate; + this.options.headers['X-Line-Access'] = config.tokenn; + this.options.path = this.config.LINE_COMMAND_PATH; + this.setTHttpClient(this.options); + this.options.headers['User-Agent'] = 'Line/7.18.1'; + this.axz = true; + this.setTHttpClient(this.options); + this.axz = false; + resolve({ authToken, certificate, verifiedQr }); + }) + }); + }); + }); + } + + + _xlogin(id,password){ + const pinVerifier = new PinVerifier(id, password); + return new Promise((resolve, reject) => ( + this._setProvider(id).then(() => { + this.setTHttpClient(); + this._getRSAKeyInfo(this.provider, (key, credentials) => { + this.options.path = this.config.LINE_RS; + this.setTHttpClient(this.options); + const rsaCrypto = pinVerifier.getRSACrypto(credentials); + reqx.type = 0; + reqx.identityProvider = this.provider; + reqx.identifier = rsaCrypto.keyname; + reqx.password = rsaCrypto.credentials; + reqx.keepLoggedIn = true; + reqx.accessLocation = this.config.ip; + reqx.systemName = 'Js-Kicker'; + reqx.e2eeVersion = 0; + try{ + this._client.loginZ(reqx, + (err,success) => { + if (err) { + console.log('\n\n'); + console.error("=> "+err.reason); + process.exit(); + } + this.options.path = this.config.LINE_COMMAND_PATH; + this.setTHttpClient(this.options); + this._authConn(); + this._client.pinCode = success.pinCode; + console.info("\n\n=============================\nEnter This Pincode => "+success.pinCode+"\nto your mobile phone in 2 minutes\n============================="); + this._checkLoginResultType(success.type, success); + reqxy.type = 1; + this._loginWithVerifier((verifierResult) => { + this.options.path = this.config.LINE_COMMAND_PATH; + this.setTHttpClient(this.options); + config.tokenn = verifierResult.authToken; + this._checkLoginResultType(verifierResult.type, verifierResult); + resolve(verifierResult); + }); + }); + }catch(error) { + console.log('error'); + console.log(error); + } + }) + }) + )); + } + + async _loginWithVerifier(callback) { + let retx = await this.getJson(this.config.LINE_CERTIFICATE_URL) + reqxy.verifier = retx.result.verifier; + this._authService.loginZ(reqxy,(err,success) => { + callback(success); + }) + } + + _setProvider(id) { + this.provider = this.config.EMAIL_REGEX.test(id) ? + IdentityProvider.LINE : + IdentityProvider.NAVER_KR; + + return this.provider === IdentityProvider.LINE ? + this.getJson(this.config.LINE_SESSION_LINE_URL) : + this.getJson(this.config.LINE_SESSION_NAVER_URL); + } + + async _getRSAKeyInfo(provider, callback){ + let result = await this._client.getRSAKeyInfo(provider); + callback(result.keynm, result); + } + + _checkLoginResultType(type, result) { + this.config.Headers['X-Line-Access'] = result.authToken || result.verifier; + if (result.type === LoginResultType.SUCCESS) { + this.certificate = result.certificate; + this.authToken = result.authToken; + } else if (result.type === LoginResultType.REQUIRE_QRCODE) { + console.log('require QR code'); + } else if (result.type === LoginResultType.REQUIRE_DEVICE_CONFIRM) { + console.log('require device confirm'); + } else { + throw new Error('unkown type'); + } + return result; + } + + async _sendMessage(message, txt ,seq = 0) { + message.text = txt; + return await this._client.sendMessage(0, message); + } + + _kickMember(group,memid) { + return this._client.kickoutFromGroup(0,group,memid); + } + + _cancel(groupid,member) { + return this._client.cancelGroupInvitation(0,groupid,member); + } + + async _getGroupsJoined() { + return await this._client.getGroupIdsJoined() + } + + async _getGroupsInvited() { + return await this._client.getGroupIdsInvited(); + } + + async _myProfile() { + return await this._client.getProfile(); + } + + _inviteIntoGroup(group,memid) { + return this._client.inviteIntoGroup(0,group,memid); + } + + async _findGroupByName(name) { + let group = []; + let groupID = await this._getGroupsJoined(); + let groups = await this._getGroups(groupID); + for (let key in groups) { + if(groups[key].name === name){ + group.push(groups[key]); + } + } + return group; + + } + + async _refreshGroup() { + await this._getGroupsInvited(); + await this._getGroupsJoined(); + return; + } + + _rejectGroupInvitation(groupIds) { + return this._client.rejectGroupInvitation(0,groupIds); + } + + async _createGroup(groupName,members) { + await this._getAllContactIds(); + return this._client.createGroup(0,groupName,members); + } + + async _getAllContactIds(){ + return await this._client.getAllContactIds(); + } + + async _createRoom(memberids) { + return await this._client.createRoom(0,[memberids]); + } + + async _acceptGroupInvitation(groupid) { + this._client.acceptGroupInvitation(0,groupid); + await this._refreshGroup(); + return; + } + + _invite(group,member) { + return this._client.inviteIntoGroup(0, group, member) + } + + async _updateGroup(group) { + return await this._client.updateGroup(0, group) + } + + async _updateProfile(profile) { + return await this._client.updateProfile(0, profile) + } + + _getContacts(mid) { + return this._client.getContacts(mid) + } + + async _getGroups(groupId) { + return await this._client.getGroups(groupId); + } + + async _getGroup(groupId) { + return await this._client.getGroup(groupId); + } + + _leaveGroup(gid) { + return this._client.leaveGroup(0,gid); + } + + async _reissueGroupTicket(groupId) { + return await this._client.reissueGroupTicket(groupId); + } + + async _findGroupByTicket(ticketID){ + return await this._client.findGroupByTicket(ticketID); + } + + async _acceptGroupInvitationByTicket(gid,ticketID){ + this._refreshGroup(); + return await this._client.acceptGroupInvitationByTicket(0,gid,ticketID); + } + + async _sendFileByUrl(message,uri) { + let media = 1; + if (!fs.existsSync(__dirname+'/tmp')){ + await fs.mkdirSync(__dirname+'/tmp'); + } + let head = await unirest.head(uri,async (res) => { + let formatFile = res.headers['content-type'].split('/')[1].toLowerCase(); + let locationFile = __dirname + `/tmp/${Math.floor(Math.random() * 100)}.${formatFile}`; + await unirest.get(uri).end().pipe(fs.createWriteStream(locationFile)); + return this._sendFile(message,locationFile,media); + }); + } + + async _sendImageByUrl(message,uri) { + await this._sendFileByUrl(message,uri); + } + + async _sendImage(message, filePath) { + this._sendFile(message,filePath, 1); + } + + async _download(uri,name,type) { + let formatType; + switch (type) { + case 3: + formatType = 'm4a'; + break; + default: + formatType = 'jpg'; + break; + } + let dir = __dirname+'/../download'; + if (!fs.existsSync(dir)){ + await fs.mkdirSync(dir); + } + await unirest + .get(uri) + .headers({ + ...this.config.Headers + }) + .end((res) => { + if(res.error) { + console.log(res.error); + return 'err'; + } + }).pipe(fs.createWriteStream(`${dir}/${name}.${formatType}`)); + } + + async _sendFile(message,filepaths, typeContent = 1) { + let filename = 'media'; + let typeFile; + + switch (typeContent) { + case 2: + typeFile = 'video' + break; + case 3: + typeFile = 'audio' + break; + default: + typeFile = 'image' + break; + } + + let M = new Message(); + M.to = message.to; + M.contentType= typeContent; + M.contentPreview= null; + M.contentMetadata= null; + + + const filepath = path.resolve(__dirname,filepaths) + console.log('File Locate on',filepath); + fs.readFile(filepath,async (err, bufs) => { + let imgID = await this._client.sendMessage(0,M); + const data = { + params: JSON.stringify({ + name: filename, + oid: imgID.id, + size: bufs.length, + type: typeFile, + ver: '1.0' + }) + }; + return this + .postContent(config.LINE_POST_CONTENT_URL, data, filepath) + .then((res) => { + if(res.err) { + console.log('err',res.error) + return; + } + console.log(res.headers); + if(filepath.search(/download\//g) === -1) { + fs.unlink(filepath, (err) => { + if (err) { + console.log('err on upload',err); + return err + }; + console.log(`successfully deleted ${filepath}`); + }); + } + + }); + }); + } + + postContent(url, data = null, filepath = null) { + return new Promise((resolve, reject) => ( + unirest.post(url) + .headers({ + ...this.config.Headers, + 'Content-Type': 'multipart/form-data' + }) + .timeout(120000) + .field(data) + .attach('files', filepath) + .end((res) => { + if(res.err) { + console.error('error on post to server'); + reject(res.error); + return; + } + resolve(res) + }) + )); + } + + _fetchOperations(revision, count = 5) { + // this.options.path = this.config.LINE_POLL_URL + return this._client.fetchOperations(revision, count); + } + + _fetchOps(revision, count = 5) { + return this._client.fetchOps(revision, count,0,0); + } + + getJson(path) { + return new Promise((resolve, reject) => ( + unirest.get(`https://${this.config.LINE_DOMAIN}${path}`) + .headers(this.config.Headers) + .timeout(120000) + .end((res) => ( + res.error ? reject(res.error) : resolve(res.body) + )) + )); + } +} + +module.exports = LineAPI; diff --git a/src/command.js b/src/command.js new file mode 100644 index 0000000..35d9c5b --- /dev/null +++ b/src/command.js @@ -0,0 +1,80 @@ +const LineAPI = require('./api'); + +let exec = require('child_process').exec; + +class Command extends LineAPI { + + constructor() { + super(); + this.spamName = []; + } + + get payload() { + if(typeof this.messages !== 'undefined'){ + return (this.messages.text !== null) ? this.messages.text.split(' ').splice(1) : '' ; + } + return false; + } + + async getProfile() { + let { displayName } = await this._myProfile(); + return displayName; + } + + async cancelMember() { + let groupID; + if(this.payload.length > 0) { + let [ groups ] = await this._findGroupByName(this.payload.join(' ')); + groupID = groups.id; + } + let gid = groupID || this.messages.to; + let { listPendingInvite } = await this.searchGroup(gid); + if(listPendingInvite.length > 0){ + this._cancel(gid,listPendingInvite); + } + } + + async searchGroup(gid) { + let listPendingInvite = []; + let thisgroup = await this._getGroups([gid]); + if(thisgroup[0].invitee !== null) { + listPendingInvite = thisgroup[0].invitee.map((key) => { + return key.mid; + }); + } + let listMember = thisgroup[0].members.map((key) => { + return { mid: key.mid, dn: key.displayName }; + }); + return { + listMember, + listPendingInvite + } + } + + async getSpeed() { + let curTime = Date.now() / 1000; + await this._sendMessage(this.messages, 'waiting...'); + const rtime = (Date.now() / 1000) - curTime; + await this._sendMessage(this.messages, `${rtime} Second`); + return; + } + + async kickAll() { + let groupID; + if(this.stateStatus.kick == 1) { + let updateGroup = await this._getGroup(this.messages.to); + updateGroup.name = '𝔑𝔬 ℌ𝔢𝔞𝔡'; + await this._updateGroup(updateGroup); + this._sendMessage(this.messages, 'Ngantos Kepanggih Malih Asu'); + let { listMember } = await this.searchGroup(this.messages.to); + for (var i = 0; i < listMember.length; i++) { + if(!this.isAdminOrBot(listMember[i].mid)){ + this._kickMember(this.messages.to,[listMember[i].mid]) + } + } + return; + } + } +} + +module.exports = Command; diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..3de25dd --- /dev/null +++ b/src/main.js @@ -0,0 +1,84 @@ +const Command = require('./command'); +const { Message, OpType, Location, Profile } = require('../curve-thrift/line_types'); + +class LINE extends Command { + constructor() { + super(); + this.receiverID = ''; + this.checkReader = []; + this.stateStatus = { + cancel: 1, + kick: 1, + salam: 1 + }; + this.messages; + this.payload + } + + get myBot() { + const bot = ['u1d55aeaa8b863cb338f4e8fd7a761b4b','u15ea9e62d7293bc69654e5e5f8a0053b','u277c188afb3f7632c261c58a79933360','u27dcf89bce7c8747107301e7b4424e08','u3a1641641abbf666b61a09221748d10c','u413abe1a4bbe3f646c8edab032fd6117','u43a4a9e76c47863e4a839b39b95c7047','u5ed385b3f578a1552439d75ada3eaea4','u605205b7305427eb778b499dbf4aebc8','u630ee831febbe7240973364570541171','u9882ac8a197b20f25cc21650f656fadd','ub1594fa0d3959eb82ac73675541d7ee0','ubc56b8fda25e78795ce157905a7fe6f2','ube3899df6730441eae28c3dc2f109249','uc687709232ddb16870499e0e9687c8ed','ue7afb5f5fd1b16902f22f29b9c9c9970','ufedfd6819d63f9235007cab688acebe7','u382280cbc27f30b9393b17d87ac4c569']; + return bot; + } + + isAdminOrBot(param) { + return this.myBot.includes(param); + } + + getOprationType(operations) { + for (let key in OpType) { + if(operations.type == OpType[key]) { + if(key !== 'NOTIFIED_UPDATE_PROFILE') { + console.info(`[* ${operations.type} ] ${key} `); + } + } + } + } + + poll(operation) { + if(operation.type == 25 || operation.type == 26) { + let message = new Message(operation.message); + this.receiverID = message.to = (operation.message.to === this.myBot[0]) ? operation.message._from : operation.message.to ; + Object.assign(message,{ ct: operation.createdTime.toString() }); + this.textMessage(message) + } + + if(operation.type == 13) { + if(this.stateStatus.kick == 1) { + return this._acceptGroupInvitation(operation.param1); + } + } + this.getOprationType(operation); + } + + command(msg, reply) { + if(this.messages.text !== null) { + if(this.messages.text === msg.trim()) { + if(typeof reply === 'function') { + reply(); + return; + } + if(Array.isArray(reply)) { + reply.map((v) => { + this._sendMessage(this.messages, v); + }) + return; + } + return this._sendMessage(this.messages, reply); + } + } + } + + async textMessage(messages) { + this.messages = messages; + let payload = (this.messages.text !== null) ? this.messages.text.split(' ').splice(1).join(' ') : '' ; + let receiver = messages.to; + let sender = messages.from; + + this.command('speed', this.getSpeed.bind(this)); + this.command(`huhu ${payload}`,this.cancelMember.bind(this)); + } + +} + + +module.exports = LINE;