Add files via upload
gonna rework it soon, to fix bugs and put everything in classes. it all started as a dirty testmaster
parent
c5986f3eb4
commit
eb49e42ae1
@ -0,0 +1,19 @@
|
||||
const { Airgram, Auth, prompt } = require('airgram')
|
||||
const airgram = new Airgram({
|
||||
apiId: 1203086,
|
||||
apiHash: '213bcfbdae59ef171d85cade070fc33d',
|
||||
|
||||
useChatInfoDatabase: true,
|
||||
useMessageDatabase: true,
|
||||
databaseDirectory: './db/',
|
||||
useSecretChats: false,
|
||||
|
||||
|
||||
logVerbosityLevel: 2, // 2,
|
||||
|
||||
deviceModel: 'by @cat6e',
|
||||
systemVersion: '1.0.0',
|
||||
applicationVersion: '1.0.0',
|
||||
})
|
||||
|
||||
module.exports = airgram
|
@ -0,0 +1,698 @@
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
// #tgfusefs:dirData:fileName:fuseData:attributes
|
||||
const { Airgram, Auth, prompt, toObject } = require( 'airgram' )
|
||||
const airgram = require('./airgram')
|
||||
const G_UID = process.getuid ? process.getuid() : 0
|
||||
const G_GID = process.getgid ? process.getgid() : 0
|
||||
const FILEMODE = {
|
||||
RO: 33024,
|
||||
400: 33024,
|
||||
444: 33060,
|
||||
666: 33206,
|
||||
777: 33279,
|
||||
PRWRR: 4516, // Pipe
|
||||
}
|
||||
const cachePrefix = new Date().valueOf()
|
||||
airgram.use(new Auth({
|
||||
code: () => prompt(`Please enter the secret code:\n`),
|
||||
phoneNumber: () => prompt(`Please enter your phone number:\n`),
|
||||
password: () => prompt(`Please enter your pw:\n`)
|
||||
}))
|
||||
const cacheManager = require('cache-manager')
|
||||
const CACHE = memoryCache = cacheManager.caching({store: 'memory', max: 100, ttl: 10/*seconds*/});
|
||||
|
||||
const virtualPipes = {}
|
||||
const virtualUploads = {}
|
||||
const virtualUploadMap = []
|
||||
//const virtualUploadMapReverse = {}
|
||||
|
||||
|
||||
const fuse = require('node-fuse-bindings')
|
||||
const mountPath = '/home/user/telegram'
|
||||
/**
|
||||
* mkdir /tgfusefstmp
|
||||
* mount -t tmpfs -o size=1G,nr_inodes=10k,mode=777 tmpfs /tgfusefstmp
|
||||
*/
|
||||
const tempMountPath = '/home/user/temp'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
async function main() {
|
||||
const me = toObject(await airgram.api.getMe())
|
||||
console.log(`[Me] `, me.id)
|
||||
const chats = toObject(await airgram.api.getChats({
|
||||
chatList: { _: 'chatListMain' },
|
||||
limit: 100,
|
||||
}))
|
||||
/*let uploadFileRequest = await toObject(airgram.api.sendMessage({
|
||||
chatId: 777000,
|
||||
inputMessageContent: {
|
||||
_: 'inputMessageDocument',
|
||||
document: { _: 'inputFileLocal', path: '/root/test' },
|
||||
caption: {
|
||||
_: 'formattedText',
|
||||
text: `#tgfusefs💾${ 'test.fifo' }`
|
||||
}
|
||||
},
|
||||
}))
|
||||
console.log('uploadFileRequest', uploadFileRequest)*/
|
||||
/*{
|
||||
file: {
|
||||
_: 'inputFileGenerated',
|
||||
originalPath: '',
|
||||
conversion: '#tgfuse',
|
||||
expectedSize: 0,
|
||||
},
|
||||
fileType: {
|
||||
_: 'fileTypeDocument'
|
||||
},
|
||||
priority: 15,
|
||||
}*/
|
||||
console.log('Done')
|
||||
//process.exit(0)
|
||||
}
|
||||
|
||||
const EventEmitter = require('events');
|
||||
class TGFileUploadEmitter extends EventEmitter {}
|
||||
const tgFileUploadEmitter = new TGFileUploadEmitter()
|
||||
|
||||
const asyncRedis = require("async-redis")
|
||||
const client = asyncRedis.createClient()
|
||||
|
||||
airgram.on('updateMessageSendAcknowledged', ({ update }) => {
|
||||
console.log(update)
|
||||
})
|
||||
airgram.on('updateMessageSendFailed', ({ update }) => {
|
||||
console.log(update)
|
||||
tgFileUploadEmitter.emit(`failed:${ update.oldMessageId }`, update)
|
||||
})
|
||||
airgram.on('updateMessageSendSucceeded', ({ update }) => {
|
||||
//console.log(update)
|
||||
tgFileUploadEmitter.emit(`finished:${ update.oldMessageId }`, update)
|
||||
})
|
||||
airgram.on('uploadFile', ({ update }) => {
|
||||
console.log(update)
|
||||
})
|
||||
|
||||
const resolveAndCache = (name, resolver, cacheTime=60) => memoryCache.wrap(name, resolver, { ttl: cacheTime })
|
||||
const sleep = (ms) => new Promise(res => setTimeout(res, ms))
|
||||
|
||||
async function getAllFiles(chatId, query, resourceLocator, offset, totalMessages) {
|
||||
if (!totalMessages) totalMessages = []
|
||||
console.log(query)
|
||||
let messages = await resolveAndCache(`files:${ resourceLocator }:${ offset }`, async () => {
|
||||
await sleep(500)
|
||||
console.log(`renewing cache entry for "files:${ resourceLocator }:${ offset }", newttl=${ !!offset ? 5 : 10 }`)
|
||||
return toObject(await airgram.api.searchChatMessages({
|
||||
chatId,
|
||||
filter: { _: 'searchMessagesFilterEmpty' },
|
||||
limit: !!offset ? 100 : 50,
|
||||
fromMessageId: offset || 0,
|
||||
query,
|
||||
})).messages
|
||||
}, !!offset ? 5 : 10)
|
||||
let lastMessageID
|
||||
for (let message of messages) lastMessageID = message.id
|
||||
totalMessages = totalMessages.concat(messages)
|
||||
|
||||
if (!!lastMessageID) {
|
||||
return await getAllFiles(chatId, query, resourceLocator, lastMessageID, totalMessages)
|
||||
}
|
||||
return totalMessages
|
||||
}
|
||||
const folder2FilterType = {
|
||||
'ALL': 'searchMessagesFilterEmpty',
|
||||
'animation': 'searchMessagesFilterAnimation',
|
||||
'audio': 'searchMessagesFilterAnimation',
|
||||
'document': 'searchMessagesFilterDocument',
|
||||
'photo': 'searchMessagesFilterPhoto',
|
||||
'video': 'searchMessagesFilterVideo',
|
||||
'voicenote': 'searchMessagesFilterVoiceNote',
|
||||
'photoandvideo': 'searchMessagesFilterPhotoAndVideo'
|
||||
}
|
||||
|
||||
|
||||
function getStatusHTML() {
|
||||
return '<pre>html test</pre>\n'
|
||||
}
|
||||
|
||||
function getContentSize(content) {
|
||||
switch (content._) {
|
||||
case 'messagePhoto': return getContentFileID(content.photo)
|
||||
case 'photo': return getContentFileID(content.photo.size[content.photo.size.length - 1])
|
||||
case 'photoSize': return getContentFileID(content.photo)
|
||||
|
||||
case 'messageAudio': return getContentSize(content.audio)
|
||||
case 'audio': return getContentSize(content.audio)
|
||||
|
||||
case 'messageDocument': return getContentSize(content.document)
|
||||
case 'document': return getContentSize(content.document)
|
||||
|
||||
case 'messageVideo': return getContentSize(content.video)
|
||||
case 'video': return getContentSize(content.video)
|
||||
|
||||
case 'file': return content.size
|
||||
}
|
||||
}
|
||||
function getContentFileID(content) {
|
||||
switch (content._) {
|
||||
case 'messagePhoto': return getContentFileID(content.photo)
|
||||
case 'photo': return getContentFileID(content.photo.size[content.photo.size.length - 1])
|
||||
case 'photoSize': return getContentFileID(content.photo)
|
||||
|
||||
case 'messageAudio': return getContentFileID(content.audio)
|
||||
case 'audio': return getContentFileID(content.audio)
|
||||
|
||||
case 'messageDocument': return getContentFileID(content.document)
|
||||
case 'document': return getContentFileID(content.document)
|
||||
|
||||
case 'messageVideo': return getContentFileID(content.video)
|
||||
case 'video': return getContentFileID(content.video)
|
||||
|
||||
case 'file': return content.id // getContentFileID(content.remote)
|
||||
// case 'remoteFile': return content.id
|
||||
}
|
||||
}
|
||||
|
||||
const chatDirs = {}
|
||||
async function getDirDataFromChat(chatId) {
|
||||
//console.log('getDirDataFromChat', chatId)
|
||||
let indexes = toObject(await airgram.api.searchChatMessages({
|
||||
chatId,
|
||||
// filter: { _: 'sea' },
|
||||
limit: 1,
|
||||
fromMessageId: 0,
|
||||
query: `#tgfuseindex`,
|
||||
})).messages
|
||||
if (indexes.length === 1) {
|
||||
chatDirs[chatId] = JSON.parse(indexes[0].content.text.text.split('\n')[1]) || []
|
||||
} else {
|
||||
chatDirs[chatId] = []
|
||||
}
|
||||
}
|
||||
async function saveDirDataToChat(chatId) {
|
||||
//console.log('saveDirDataToChat', chatId)
|
||||
let indexes = toObject(await airgram.api.searchChatMessages({
|
||||
chatId,
|
||||
// filter: { _: 'sea' },
|
||||
limit: 1,
|
||||
fromMessageId: 0,
|
||||
query: `#tgfuseindex`,
|
||||
})).messages
|
||||
//console.log('saveDirDataToChat indexes.length', indexes.length)
|
||||
chatDirs[chatId] = chatDirs[chatId] || []
|
||||
if (indexes.length > 0) {
|
||||
let deleteResponse = await airgram.api.deleteMessages({
|
||||
chatId,
|
||||
messageIds: indexes.map(x => x.id),
|
||||
revoke: true,
|
||||
})
|
||||
//console.log('deleting old msg', chatId)
|
||||
}
|
||||
let newIndexResponse = toObject(await airgram.api.sendMessage({
|
||||
chatId,
|
||||
inputMessageContent: {
|
||||
_: 'inputMessageText',
|
||||
text: {
|
||||
_: 'formattedText', text: `#tgfuseindex\n${ JSON.stringify(chatDirs[chatId]) }`
|
||||
}
|
||||
},
|
||||
}))
|
||||
//console.log('saveDirDataToChat', newIndexResponse)
|
||||
}
|
||||
function getDirData(path, notAFileName=false) {
|
||||
//if (path.split('/').length === 4) return path.split('/')[3]
|
||||
// const p = require('path').parse(path.split('/').splice(3))
|
||||
let p = path.split('/').splice(3).map(x => Buffer.from(x).toString('base64')).join('/')
|
||||
p = p.length === 0 ? '$' : p
|
||||
console.log('getDirData', path, p)
|
||||
return p
|
||||
}
|
||||
let fdCounter = 0
|
||||
const FD_RANGE = { REAL: 10, VIRTUAL: 50 }
|
||||
function getFDHandle(offset) {
|
||||
fdCounter = (fdCounter + 1) % 30
|
||||
console.log('[FD HANDLER] next handle is ', fdCounter+ offset )
|
||||
return fdCounter + offset
|
||||
}
|
||||
fuse.mount(mountPath, {
|
||||
readdir: async (path, cb) => {
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
console.log(deepness, p, p.dir.substring(1).split('/'))
|
||||
|
||||
console.log('readdir(%s)', path)
|
||||
if (path === '/') { // first folder
|
||||
let chatList = await resolveAndCache('chatList', async () => {
|
||||
return toObject(await airgram.api.getChats({
|
||||
offsetOrder: '9223372036854775807',
|
||||
offsetChatId: 0,
|
||||
limit: 500
|
||||
})).chatIds
|
||||
})
|
||||
return cb(0, ['status.html'].concat(chatList))
|
||||
}
|
||||
// Chat ID Directorys
|
||||
if (deepness === 1 && path.split('/').length == 2) {
|
||||
return cb(0, ['title', 'json', 'root'])
|
||||
}
|
||||
// Chat FilterType Files Directorys
|
||||
if (deepness >= 1 && path.split('/').length >= 3 && path.indexOf('root') > 0) {
|
||||
// console.log('every folder in the chatfolder, listing the files corresponding to the type')
|
||||
const chatId = parseInt(p.dir.split('/')[1])
|
||||
await resolveAndCache(`dirIndex:${ chatId }`, async () => await getDirDataFromChat(parseInt(chatId)))
|
||||
let dirs = chatDirs[chatId] || []
|
||||
const dirData = getDirData(path, true)
|
||||
console.log('[VDIR] dirs=', dirs, '| dirData=', dirData)
|
||||
dirs = dirs.filter(dirName => {
|
||||
if (dirData === '$') return dirName.indexOf('/') === -1
|
||||
console.log(dirName, dirName.split('/').length, dirData, dirData.split('/').length)
|
||||
return dirName.length > dirData.length &&
|
||||
dirName.indexOf(dirData) === 0 &&
|
||||
dirName.split('/').length === dirData.split('/').length + 1
|
||||
})
|
||||
.map(x => {
|
||||
x = x.indexOf('/') === -1 ? x : x.split('/')[x.split('/').length - 1]
|
||||
return Buffer.from(x, 'base64').toString('utf8')
|
||||
})
|
||||
|
||||
let files = await getAllFiles(parseInt(p.dir.substr(1)), `#tgfusefs:${ dirData }${ dirData === '$' ? ':' : ''}`, `${ p.dir }:${ p.base }`)
|
||||
//console.log(`files:${ p.dir }:${ p.base }`, files)
|
||||
// files folder folder
|
||||
console.log('---')
|
||||
return cb(0, files.map(message => {
|
||||
// console.log(message.content.caption.text.split(':'))
|
||||
return Buffer.from(message.content.caption.text.split(':')[2], 'base64').toString('utf8')
|
||||
}).concat(dirs))
|
||||
}
|
||||
return cb(0, [])
|
||||
},
|
||||
mkdir: async (path, mode, cb) => {
|
||||
console.log('mkdir(%s, %d)', path, mode)
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
try {
|
||||
const chatId = parseInt(p.dir.split('/')[1])
|
||||
if (deepness >= 1 && path.split('/').length > 3 && path.indexOf('root') > 0) {
|
||||
const dirData = getDirData(path, true)
|
||||
console.log('mkdir', chatId, path, dirData)
|
||||
chatDirs[chatId] = chatDirs[chatId] || []
|
||||
if (chatDirs[chatId].indexOf(dirData) < 0) {
|
||||
chatDirs[chatId].push(dirData)
|
||||
await saveDirDataToChat(chatId)
|
||||
await client.set(cachePrefix+'-'+chatId+'-'+dirData, 1)
|
||||
}
|
||||
}
|
||||
return cb(0)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return cb(fuse.ENOENT)
|
||||
}
|
||||
},
|
||||
rmdir: async (path, cb) => {
|
||||
console.log('rmdir(%s)', path)
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
try {
|
||||
const chatId = parseInt(p.dir.split('/')[1])
|
||||
if (deepness >= 1 && path.split('/').length > 3 && path.indexOf('root') > 0) {
|
||||
const dirData = getDirData(path, true)
|
||||
console.log('rmdir', chatId, path, dirData)
|
||||
chatDirs[chatId] = chatDirs[chatId] || []
|
||||
if (chatDirs[chatId].indexOf(dirData) > -1) {
|
||||
chatDirs[chatId].splice(chatDirs[chatId].indexOf(dirData), 1)
|
||||
await saveDirDataToChat(chatId)
|
||||
await client.set(chatId+'-'+dirData, 0)
|
||||
}
|
||||
}
|
||||
return cb(0)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return cb(fuse.ENOENT)
|
||||
}
|
||||
},
|
||||
getattr: async (path, cb) => {
|
||||
if (!!virtualPipes[path]) {
|
||||
return cb(0, {
|
||||
mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID,
|
||||
nlink: 1, mode: FILEMODE[666], // fifo pipe
|
||||
size: 10,
|
||||
})
|
||||
}
|
||||
if (path === '/status.html') {
|
||||
return cb(0, {
|
||||
mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID,
|
||||
nlink: 1, mode: FILEMODE[444],
|
||||
size: getStatusHTML().length,
|
||||
})
|
||||
}
|
||||
try {
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
const chatId = parseInt(p.dir.split('/')[1])
|
||||
console.log('getattr(%s)', path, deepness, path.split('/').length, 'chatId=' + chatId)
|
||||
if (!!chatId && chatId > 0) {
|
||||
await resolveAndCache(`dirIndex:${ chatId }`, async () => await getDirDataFromChat(chatId))
|
||||
const dirData = getDirData(path, true)
|
||||
console.log ('VDIR DATA SEARCHED', dirData, chatDirs[chatId] || [])
|
||||
let existInCache = false
|
||||
try {
|
||||
existInCache = await client.get(cachePrefix+'-'+chatId+'-'+dirData) == 1
|
||||
console.log('existInCache', existInCache)
|
||||
} catch (e) { }
|
||||
if (existInCache || (chatDirs[chatId] || []).indexOf(dirData) > -1 ) {
|
||||
console.log('[VDIR] emulating', path, existInCache, (chatDirs[chatId] || []).indexOf(dirData))
|
||||
console.log('---')
|
||||
return cb(0, {
|
||||
mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID,
|
||||
nlink: 1,
|
||||
size: 0,
|
||||
mode: 16877, // dir
|
||||
})
|
||||
}
|
||||
}
|
||||
if (deepness === 1 && p.name === 'title' && p.ext === '') {
|
||||
let title = await resolveAndCache(`title:${ p.dir }`, async () => toObject(await airgram.api.getChat({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
})).title)
|
||||
return cb(0, {
|
||||
mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID,
|
||||
nlink: 1, mode: FILEMODE[444], // readonly file
|
||||
size: title.length + 1, // + 1 for nullbyte
|
||||
})
|
||||
}
|
||||
if (deepness === 1 && p.name === 'json' && p.ext === '') {
|
||||
let json = await resolveAndCache(`json:${ p.dir }`, async () => JSON.stringify(toObject(await airgram.api.getChat({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
})), null, '\t'))
|
||||
return cb(0, {
|
||||
mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID,
|
||||
nlink: 1, mode: FILEMODE[444], // readonly file
|
||||
size: json.length + 1, // + 1 for nullbyte
|
||||
})
|
||||
}
|
||||
// every folder in the chatfolder, its size = amount of files in there
|
||||
if (deepness === 1 && path.split('/').length == 3 && p.base === 'root') {
|
||||
console.log('every folder in the chatfolder, its size = amount of files in there')
|
||||
/*if (p.base === 'files') return cb(0, { mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID, nlink: 1, mode: 16877, size: 0, })
|
||||
let fileTypeCount = await resolveAndCache(`count:${ p.dir }:${ p.base }`, async () => toObject(await airgram.api.getChatMessageCount({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
filter: { _: 'searchMessagesFilterEmpty' },
|
||||
returnLocal: false
|
||||
})).count, 5)
|
||||
console.log(`count:${ p.dir }:${ p.base }`, fileTypeCount)
|
||||
*/
|
||||
// files folder folder
|
||||
return cb(0, {
|
||||
mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID,
|
||||
nlink: 1, mode: 16877, // dir
|
||||
size: 0,
|
||||
})
|
||||
}
|
||||
if (deepness === 1) { // first folder
|
||||
return cb(0, {
|
||||
mtime: new Date(), atime: new Date(), ctime: new Date(), uid: G_UID, gid: G_GID,
|
||||
nlink: 1,
|
||||
size: 0,
|
||||
mode: 16877, // dir
|
||||
})
|
||||
}
|
||||
// show file stats for specific file in type dir
|
||||
if (deepness >= 2 && path.split('/').length >= 4 && p.dir.split('/')[2] === 'root') {
|
||||
const dirData = getDirData(path)
|
||||
console.log('dirData', dirData)
|
||||
//let files = await getAllFiles(parseInt(p.dir.substr(1)), folder2FilterType[ p.base ], `${ p.dir }:${ p.base }`)
|
||||
let getattr = await resolveAndCache(`getattr:${ path }`, async () => {
|
||||
const query = `#tgfusefs:${ dirData }:${ Buffer.from(p.base).toString('base64') }:`
|
||||
console.log ('searching for ===' + query)
|
||||
return toObject(await airgram.api.searchChatMessages({
|
||||
chatId,
|
||||
// filter: { _: 'sea' },
|
||||
limit: 1,
|
||||
fromMessageId: 0,
|
||||
query,
|
||||
})).messages[0]
|
||||
}, 15)
|
||||
if (!getattr) { console.log('no file found, ', path); return cb(fuse.ENOENT) }
|
||||
console.log('---', getattr.id)
|
||||
return cb(0, {
|
||||
mtime: new Date(getattr.date*1e3), atime: new Date(getattr.date*1e3), ctime: new Date(getattr.date*1e3), uid: G_UID, gid: G_GID,
|
||||
nlink: 1, mode: FILEMODE[666], // file
|
||||
size: getContentSize(getattr.content),
|
||||
})
|
||||
}
|
||||
// else anything
|
||||
console.log('nothing is matching for readdir')
|
||||
return cb(fuse.ENOENT)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return cb(fuse.ENOENT)
|
||||
}
|
||||
},
|
||||
open: async (path, flags, cb) => {
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
console.log('open(%s, %d)', path, flags)
|
||||
if (!!virtualPipes[path]) return cb(0, getFDHandle(FD_RANGE.VIRTUAL)) // virtual file handles are from 50-79
|
||||
if (deepness === 1 && p.name === 'title' && p.ext === '') return cb(0, 1)
|
||||
if (deepness === 1 && p.name === 'json' && p.ext === '') return cb(0, 2)
|
||||
if (path === '/status.html') return cb(0, 4)
|
||||
|
||||
cb(0, getFDHandle(FD_RANGE.REAL)) // real file handles are from 10-39
|
||||
},
|
||||
read: async (path, fd, buf, len, pos, cb) => {
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
console.log('read(%s, %d, %d, %d)', path, fd, len, pos)
|
||||
//console.log(p, deepness)
|
||||
|
||||
if (fd === 1 && deepness === 1 && p.name === 'title' && p.ext === '') {
|
||||
let title = await resolveAndCache(`title:${ p.dir }`, async () => toObject(await airgram.api.getChat({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
})).title)
|
||||
|
||||
let str = title.slice(pos, pos + len)
|
||||
if (!str) return cb(0)
|
||||
buf.write(str)
|
||||
return cb(str.length)
|
||||
}
|
||||
if (fd === 2 && deepness === 1 && p.name === 'json' && p.ext === '') {
|
||||
let json = await resolveAndCache(`json:${ p.dir }`, async () => JSON.stringify(toObject(await airgram.api.getChat({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
})), null, '\t'))
|
||||
|
||||
let str = json.slice(pos, pos + len)
|
||||
if (!str) return cb(0)
|
||||
buf.write(str)
|
||||
return cb(str.length)
|
||||
}
|
||||
if (fd >= FD_RANGE.VIRTUAL && !!virtualPipes[path]) {
|
||||
}
|
||||
if (fd === 4) {
|
||||
let str = getStatusHTML().slice(pos, pos + len)
|
||||
if (!str) return cb(0)
|
||||
buf.write(str)
|
||||
return cb(str.length)
|
||||
}
|
||||
if (fd >= FD_RANGE.REAL && deepness >= 2 && path.split('/').length >= 4 && p.dir.split('/')[2] === 'root') {
|
||||
// show file stats for specific file in type dir
|
||||
//let files = await getAllFiles(parseInt(p.dir.substr(1)), folder2FilterType[ p.base ], `${ p.dir }:${ p.base }`)
|
||||
let getattr = await resolveAndCache(`getattr:${ path }`, async () => toObject(await airgram.api.searchChatMessages({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
// filter: { _: 'searchMessagesFilterEmpty' },
|
||||
limit: 1,
|
||||
fromMessageId: 0,
|
||||
query: `#tgfusefs:${ getDirData(p.dir) }:${ Buffer.from(p.base).toString('base64') }`,
|
||||
})).messages[0], 15)
|
||||
let fileId = getContentFileID(getattr.content)
|
||||
let downloadRequest = toObject(await airgram.api.downloadFile({
|
||||
fileId, priority: 30, offset: pos, limit: len, synchronous: true
|
||||
}))
|
||||
return fs.open(downloadRequest.local.path, 'r', function(err, fd) {
|
||||
// /----- where to start writing at in `buffer`
|
||||
fs.readSync(fd, buf, 0, len, pos)
|
||||
// \------- where to read from in the file given by `fd`
|
||||
return cb(buf.length)
|
||||
})
|
||||
}
|
||||
|
||||
let str = '< Empty >\n'.slice(pos, pos + len)
|
||||
if (!str) return cb(0)
|
||||
buf.write(str)
|
||||
return cb(str.length)
|
||||
},
|
||||
release: async (path, fd, cb) => {
|
||||
console.log('release(%s, %d)', path, fd)
|
||||
if (!!virtualPipes[path]) {
|
||||
const virtualPipe = virtualPipes[path]
|
||||
console.log(virtualPipes[path])
|
||||
fs.closeSync(virtualPipes[path].fd)
|
||||
const b64Name = Buffer.from(require('path').parse(virtualPipe.tempTargetFile).base).toString('base64')
|
||||
let uploadFileRequest = toObject(await airgram.api.sendMessage({
|
||||
chatId: 777000,
|
||||
inputMessageContent: {
|
||||
_: 'inputMessageDocument',
|
||||
document: { _: 'inputFileLocal', path: virtualPipe.tempTargetFile },
|
||||
caption: {
|
||||
_: 'formattedText', text: `#tgfusefs:${ getDirData(virtualPipe.targetPath) }:${ b64Name }:fuseData:attributes`
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
console.log('uploadFileRequest', uploadFileRequest.sendingState)
|
||||
try {
|
||||
//console.log(clipJSON)
|
||||
tgCloud = await new Promise((res, rej) => {
|
||||
tgFileUploadEmitter.removeAllListeners()
|
||||
tgFileUploadEmitter.once(`finished:${ uploadFileRequest.id }`, res)
|
||||
tgFileUploadEmitter.once(`failed:${ uploadFileRequest.id }`, rej)
|
||||
})
|
||||
// console.log('upload emitter', tgCloud)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return cb(fuse.ENOENT)
|
||||
}
|
||||
fs.unlinkSync(virtualPipes[path].tempTargetFile)
|
||||
// uploadFile
|
||||
delete virtualPipes[path]
|
||||
return cb(0)
|
||||
}
|
||||
return cb(0)
|
||||
},
|
||||
unlink: async (path, cb) => {
|
||||
try {
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
console.log('unlink(%s)', path)
|
||||
|
||||
if (deepness === 2 && path.split('/').length == 4 && p.dir.split('/')[2] === 'root') {
|
||||
let unlink = await resolveAndCache(`unlink:${ path }`, async () => toObject(await airgram.api.searchChatMessages({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
filter: { _: folder2FilterType[ p.dir.split('/')[2] ] },
|
||||
limit: 1,
|
||||
fromMessageId: 0,
|
||||
query: `#tgfusefs💾${ Buffer.from(p.base).toString('base64') }`,
|
||||
})).messages[0], 15)
|
||||
let deleteResponse = await airgram.api.deleteMessages({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
messageIds: [ unlink.id ],
|
||||
revoke: true,
|
||||
})
|
||||
return cb(0)
|
||||
}
|
||||
} catch (e) {
|
||||
return cb(fuse.ENOENT)
|
||||
}
|
||||
return cb(fuse.ENOENT)
|
||||
},
|
||||
rename: async (src, dest, cb) => {
|
||||
console.log('rename(%s, %s)', src, dest)
|
||||
return cb(fuse.ENOENT)
|
||||
},
|
||||
/*
|
||||
if (deepness === 2 && path.split('/').length == 4 && ['ALL', 'animation', 'audio', 'document', 'photo', 'video', 'voicenote', 'photoandvideo'].indexOf(p.dir.split('/')[2]) > - 1) {
|
||||
let rename = await resolveAndCache(`rename:${ path }`, async () => toObject(await airgram.api.searchChatMessages({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
filter: { _: folder2FilterType[ p.dir.split('/')[2] ] },
|
||||
limit: 1,
|
||||
fromMessageId: 0,
|
||||
query: `#tgfusefs💾${ p.base }`,
|
||||
})).messages[0], 15)
|
||||
let deleteREsponse = await airgram.api.deleteMessages({
|
||||
chatId: parseInt(p.dir.substr(1)),
|
||||
messageIds: [ rename.id ],
|
||||
revoke: true,
|
||||
})
|
||||
return cb(0)
|
||||
}
|
||||
return cb(fuse.ENOENT)
|
||||
},*/
|
||||
create: async (path, mode, cb) => {
|
||||
const p = require('path').parse(path),
|
||||
deepness = p.dir.substring(1).split('/').length
|
||||
console.log('create(%s, %d)', path, mode)
|
||||
if (deepness >= 2 && path.split('/').length >= 4 && p.dir.split('/')[2] === 'root') {
|
||||
//createdFiles[path] = true
|
||||
let cUUID = uuidv4()
|
||||
const tempTargetFile = require('path').join(tempMountPath, path)
|
||||
const mkdirp = require('mkdirp')
|
||||
await mkdirp(require('path').parse(tempTargetFile).dir)
|
||||
console.log('tempTargetFile', tempTargetFile)
|
||||
if (fs.existsSync(tempTargetFile)) fs.unlinkSync(tempTargetFile)
|
||||
return fs.open(tempTargetFile, 'wx', (err, fd) => {
|
||||
if (err) return console.error(err, cb(fuse.ENOENT))
|
||||
virtualUploads[cUUID] = path
|
||||
virtualPipes[path] = {
|
||||
path,
|
||||
targetPath: path,
|
||||
mode,
|
||||
cUUID,
|
||||
size: 0,
|
||||
tempTargetFile,
|
||||
fd
|
||||
}
|
||||
let virtFd = virtualUploadMap.push(path)
|
||||
return cb(0, virtFd)
|
||||
})
|
||||
/*
|
||||
let uploadFileRequest = await toObject(airgram.api.sendMessage({
|
||||
chatId: 777000,
|
||||
inputMessageContent: {
|
||||
_: 'inputMessageDocument',
|
||||
document: { _: 'inputFileLocal', expectedSize: 0, originalPath },
|
||||
caption: {
|
||||
_: 'formattedText',
|
||||
text: `#tgfusefs💾${ p.base }`
|
||||
}
|
||||
},
|
||||
}))
|
||||
let virtFd = virtualUploadMap.push(fifoPath)
|
||||
return cb(0, virtFd)
|
||||
console.log('uploadFileRequest', uploadFileRequest)
|
||||
*/
|
||||
/*
|
||||
return tgFileUploadEmitter.once('genStartCB'+cUUID, (genId) => {
|
||||
//virtualUploadMapReverse[genId] = mapIndex
|
||||
console.log('file gen created', `genid=${ genId } | virtFd=${ virtFd }`)
|
||||
cb(0, virtFd)
|
||||
})*/
|
||||
//tgFileUploadEmitter.once('genStartFail'+cUUID, () => cb(fuse.ENOENT))
|
||||
// return cb(0, 123)
|
||||
}
|
||||
return cb(fuse.ENOENT)
|
||||
},
|
||||
write: async (path, fd, buffer, length, position, cb) => {
|
||||
// console.log('write(%s, %d, buffer, %d, %d)', path, fd, length, position)
|
||||
// console.log('writing', buffer.slice(0, length))
|
||||
try {
|
||||
const bytesWritten = fs.writeSync(virtualPipes[path].fd, buffer, 0, buffer.length, position)
|
||||
return cb(bytesWritten)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return cb(fuse.ENOENT)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
}, (err) => {
|
||||
if (err) throw err
|
||||
console.log('filesystem mounted on ' + mountPath)
|
||||
})
|
||||
|
||||
process.on('SIGINT', function () {
|
||||
fuse.unmount(mountPath, function (err) {
|
||||
if (err) {
|
||||
console.log('filesystem at ' + mountPath + ' not unmounted', err)
|
||||
} else {
|
||||
console.log('filesystem at ' + mountPath + ' unmounted')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
console.log(new Date())
|
||||
main()
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "user",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"airgram": "^3.0.2",
|
||||
"async-redis": "^1.1.7",
|
||||
"cache-manager": "^3.3.0",
|
||||
"cache-manager-redis-store": "^2.0.0",
|
||||
"fuse-native": "^2.2.5",
|
||||
"mkdirp": "^1.0.4",
|
||||
"node-fuse-bindings": "^2.12.4",
|
||||
"redis": "^3.0.2",
|
||||
"uuid": "^8.1.0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue