diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index bda0a85..be4cc60 100755 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -2,43 +2,55 @@ name: Publish Docker on: release: - types: [published] + types: [published] + branches: + - 'master' + workflow_dispatch: env: REPO: hlf01/telegram-onedrive permissions: contents: read - + +run-name: ${{ github.event_name == 'workflow_dispatch' && 'build dev' || github.event.release.name }} + jobs: build: runs-on: ubuntu-latest if: github.repository_owner == 'hlf20010508' steps: - - name: Get version - id: get_version - run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - - name: Checkout uses: actions/checkout@v3 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 + with: + file: Dockerfile + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ env.REPO }}:${{ github.event.release.name }},${{ env.REPO }}:latest + if: github.event_name == 'release' && github.event.action == 'published' + + - name: Build and push image dev + uses: docker/build-push-action@v5 with: file: Dockerfile context: . platforms: linux/amd64,linux/arm64 push: true - tags: ${{ env.REPO }}:${{ steps.get_version.outputs.VERSION }},${{ env.REPO }}:latest \ No newline at end of file + tags: ${{ env.REPO }}:dev + if: github.event_name == 'workflow_dispatch' \ No newline at end of file diff --git a/bot.py b/bot.py index ffb7675..4ed6345 100644 --- a/bot.py +++ b/bot.py @@ -84,6 +84,14 @@ async def delete_message(message): await message.delete() +# if message is not edited, it will raise MessageNotModifiedError +async def edit_message(bot, event, message): + try: + await bot.edit_message(event, message) + except: + pass + + async def check_in_group(event): if isinstance(event.message.peer_id, types.PeerUser): await event.respond(''' @@ -110,7 +118,7 @@ async def res_not_login(event): await event.respond(''' You haven't logined to Telegram. ''') - await auth(event) + await auth(event, propagate=True) async def download_part(client, input_location, offset, part_size): @@ -182,7 +190,7 @@ def get_link(string): return link else: return False - except Exception: + except: return False @@ -214,7 +222,7 @@ async def help(event): @tg_bot.on(events.NewMessage(pattern="/auth", incoming=True, from_users=tg_user_name)) -async def auth(event): +async def auth(event, propagate=False): await check_in_group(event) auth_server = subprocess.Popen(('python', 'auth_server.py')) async with tg_bot.conversation(event.chat_id) as conv: @@ -268,6 +276,8 @@ async def od_code_callback(): status_bar = await conv.send_message("Status:\n\nNo job yet.") await tg_bot.pin_message(event.chat_id, status_bar) auth_server.kill() + if not propagate: + raise events.StopPropagation @tg_bot.on(events.NewMessage(pattern="/autoDelete", incoming=True, from_users=tg_user_name)) @@ -322,6 +332,8 @@ async def url(event): try: cmd = cmd_parser(event) _url = cmd[0] + # lest the url is bold + _url = _url.strip().strip('*') name = _url.split('/')[-1] except: await event.reply(''' @@ -336,33 +348,40 @@ async def url(event): raise events.StopPropagation try: + logger('upload url: %s' % _url) progress_url = onedrive.upload_from_url(_url) - print(progress_url) - progress = onedrive.upload_from_url_progress(progress_url) + logger('progress url: %s' % progress_url) + except Exception as e: + await event.reply(logger(e)) + + try: + response = onedrive.upload_from_url_progress(progress_url) + progress = response.content while progress['status'] in ['notStarted', 'inProgress']: status = "Uploaded: %.2f%%" % float(progress['percentageComplete']) logger(status) msg_link = 'https://t.me/c/%d/%d'%(event.message.peer_id.channel_id, event.message.id) - await tg_bot.edit_message(status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) + await edit_message(tg_bot, status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) await asyncio.sleep(5) - progress = onedrive.upload_from_url_progress(progress_url) + response = onedrive.upload_from_url_progress(progress_url) + progress = response.content status = "Uploaded: %.2f%%" % float(progress['percentageComplete']) logger(status) - if 'fail' not in str(progress) and 'error' not in str(progress): + if 'fail' not in str(progress) and 'error' not in str(progress) and 'Error' not in str(progress) and 'Fail' not in str(progress): logger("File uploaded to %s"%os.path.join(onedrive.remote_root_path, name)) msg_link = 'https://t.me/c/%d/%d'%(event.message.peer_id.channel_id, event.message.id) - await tg_bot.edit_message(status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) + await edit_message(tg_bot, status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) if not delete_flag: await event.reply('Done.') await delete_message(event) - await tg_bot.edit_message(status_bar, 'Status:\n\nNo job yet.') + await edit_message(tg_bot, status_bar, 'Status:\n\nNo job yet.') else: - await event.reply(logger('Error: something is wrong\n\nResponse: %s' % progress)) + await event.reply(logger('Error: something is wrong\n\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (_url, progress_url, progress))) await event.reply(logger("Analysis: try again later, or offer a proper url")) except Exception as e: - await event.reply('Error: %s\nResponse: %s' % (logger(e), logger(progress))) + await event.reply('Error: %s\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (logger(e), _url, progress_url, logger(progress))) try: if progress['errorCode'] == 'ParameterIsTooLong': await event.reply(logger("Analysis: url too long.OneDrive API doesn't support long url.")) @@ -410,7 +429,7 @@ async def callback(current, total): status = "Uploaded %.2fMB out of %.2fMB: %.2f%%"% (current, total, current / total * 100) logger(status) msg_link = 'https://t.me/c/%d/%d'%(event.message.peer_id.channel_id, event.message.id) - await tg_bot.edit_message(status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) + await edit_message(tg_bot, status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) if event.media and not isinstance(event.media, types.MessageMediaWebPage): message = await tg_client.get_messages(event.message.peer_id, ids=event.message.id) @@ -424,7 +443,7 @@ async def callback(current, total): await multi_parts_uploader(tg_client, message.media.document, name, progress_callback=callback) logger("File uploaded to %s" % os.path.join(remote_root_path, name)) await delete_message(message) - await tg_bot.edit_message(status_bar, "Status:\n\nNo job yet.") + await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") if "photo" in event.media.to_dict().keys(): if message.media: @@ -435,7 +454,7 @@ async def callback(current, total): onedrive.stream_upload(buffer, name) logger("File uploaded to %s" % os.path.join(remote_root_path, name)) await delete_message(message) - await tg_bot.edit_message(status_bar, "Status:\n\nNo job yet.") + await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") except Exception as e: await event.reply('Error: %s' % logger(e)) @@ -466,7 +485,7 @@ async def callback(current, total): await multi_parts_uploader(tg_client, message.media.document, name, progress_callback=callback) logger("File uploaded to %s" % os.path.join(remote_root_path, name)) await delete_message(event) - await tg_bot.edit_message(status_bar, "Status:\n\nNo job yet.") + await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") if "photo" in message.media.to_dict().keys(): name = "%d%s" % (message.media.photo.id, message.file.ext) @@ -474,7 +493,7 @@ async def callback(current, total): onedrive.stream_upload(buffer, name) logger("File uploaded to %s" % os.path.join(remote_root_path, name)) await delete_message(event) - await tg_bot.edit_message(status_bar, "Status:\n\nNo job yet.") + await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") except Exception as e: await event.reply('Error: %s' % logger(e)) diff --git a/onedrive.py b/onedrive.py index c61f7b1..cefd084 100644 --- a/onedrive.py +++ b/onedrive.py @@ -7,15 +7,17 @@ from onedrivesdk import HttpProvider, AuthProvider, OneDriveClient from onedrivesdk.options import HeaderOption -from onedrivesdk.error import OneDriveError +from onedrivesdk.error import OneDriveError, ErrorCode from onedrivesdk.model.upload_session import UploadSession from onedrivesdk.model.item import Item from onedrivesdk.request.item_create_session import ItemCreateSessionRequestBuilder from onedrivesdk.request.item_request_builder import ItemRequestBuilder from onedrivesdk.request_builder_base import RequestBuilderBase from onedrivesdk.request_base import RequestBase +from onedrivesdk.http_response import HttpResponse import json import asyncio +import time def authenticate_request(self, request): if self._session is None: @@ -33,6 +35,11 @@ def authenticate_request(self, request): def create_session(self, item=None): return ItemCreateSessionRequestBuilder(self.append_to_request_url("createUploadSession"), self._client, item=item) +def http_response_init(self, status, headers, content): + self._status = status + self._headers = headers + self._content = content + class Onedrive: def __init__(self, client_id, client_secret, redirect_uri, remote_root_path): api_base_url = "https://graph.microsoft.com/v1.0/" @@ -139,13 +146,29 @@ def upload_from_url(self, url): } raise OneDriveError(response_dict, response.status) - def upload_from_url_progress(self, url): - response = self.client.http_provider.send( - method="GET", - headers={}, - url=url - ) - response = json.loads(response.content) + def upload_from_url_progress(self, url): + tries = 0 + while tries < 5: + response = self.client.http_provider.send( + method="GET", + headers={}, + url=url + ) + if response.status >= 200 and response.status < 300: + break + else: + tries += 1 + time.sleep(0.1) + continue + try: + response._content = json.loads(response.content) + except: + response._content = { + "error": { + "code": ErrorCode.Malformed, + "message": "The following invalid JSON was returned:\n%s" % response.content + } + } return response @@ -220,4 +243,5 @@ def post(self, begin, length, buffer, options=None): # Overwrite the standard upload operation to use this one AuthProvider.authenticate_request = authenticate_request -ItemRequestBuilder.create_session = create_session \ No newline at end of file +ItemRequestBuilder.create_session = create_session +HttpResponse.__init__ = http_response_init \ No newline at end of file