Source code for slack_transfer.run

import argparse
import glob
import json
import os.path
import time
from ssl import SSLContext
from typing import Dict
from typing import List
from typing import Optional

import tqdm

from .client import DownloaderClient
from .client import UploaderClient


[docs]def run( local_data_dir: str, downloader_token: Optional[str] = None, uploader_token: Optional[str] = None, override: bool = False, skip_download: bool = False, skip_upload: bool = False, name_mappings: Optional[Dict[str, str]] = None, channel_names: Optional[List[str]] = None, skip_bookmarks: bool = False, ssl: Optional[SSLContext] = None, ) -> None: """See run section in :doc:`/user_guide/cli` Args: local_data_dir (str): Data directory for saving download data or loading upload data. This is required. downloader_token (str; Optional; default=None): Download token obtained from slack (the original workspace). Starts with xoxb-. This is required when skip_download=False. uploader_token (str; Optional; default=None): Upload token obtained from slack (the destination workspace). Starts with xoxb-. This is required when skip_upload=False. override (bool; Optional; default=False): This flag enables message migration to the destination workspace even when a channel already exists. This means that additional messages are inserted after the message already sent to the channel. If you want not to do so, please delete the channel on the destination workspace first. skip_download (bool; Optional; default=False): Skip download. This is usually used when the download is already finished. skip_upload (bool; Optional; default=False): Skip upload. This is usually used when only the download is necessary. name_mappings (Dict[str, str]; Optional; default=None): You can set name mappings between the channel names of the original and destination workspaces. For example, :code:`{"old_name1": "new_name1", "old_name2": "new_name2"}`. channel_names (List[str]; Optional; default=None): channel names you want to process. If not set, set to all available channels. skip_bookmarks (bool; Optional; default=False): Skip process bookmarks. ssl: (SSLContext; Optional; default=None): : An [`ssl.SSLContext`][1] instance, helpful for specifying your own custom certificate chain. """ os.makedirs(local_data_dir, exist_ok=True) if not skip_download: downloader = DownloaderClient( local_data_dir=local_data_dir, token=downloader_token, ssl=ssl ) downloader.test_connection() downloader.test_downloader() if not skip_upload: if uploader_token is None: raise ValueError("uploader_token is required") uploader = UploaderClient( local_data_dir=local_data_dir, token=uploader_token, ssl=ssl ) uploader.test_uploader() if not skip_download: channels_list: List[Dict] = downloader.download_channels_list() downloader.download_members_list() if channel_names: channels_list = list( filter(lambda x: (x["name"] in channel_names), channels_list) # type: ignore ) ts_now = int(time.time()) times_to_rest = list(map(lambda x: ts_now - x["created"], channels_list)) for i, (channel, time_to_rest) in enumerate(zip(channels_list, times_to_rest)): print(f"{i+1}/{len(channels_list)}: {channel['name']}") pbar = tqdm.tqdm(total=time_to_rest) downloader.download_channel_history( channel_id=channel["id"], channel_name=channel["name"], ts_progress_bar=pbar, ts_now=ts_now, ) pbar.close() time.sleep(1) if not skip_bookmarks: downloader.download_bookmark( channel_id=channel["id"], channel_name=channel["name"] ) if not skip_upload: if name_mappings is None: name_mappings = {} old_members = json.load( open( os.path.join(uploader.local_data_dir, "members.json"), mode="r", encoding="utf-8", ) ) old_members_dict = dict( [(member["id"], member["profile"]["real_name"]) for member in old_members] ) old_members_icon_url_dict = dict( [ ( member["id"], member["profile"]["image_original"] if "image_original" in member["profile"] else member["profile"]["image_512"], ) for member in old_members ] ) conflicts = uploader.check_upload_conflict(name_mappings=name_mappings) reverse_name_mappings = dict([(v, k) for k, v in name_mappings.items()]) conflicts = list( filter( lambda x: ( # type: ignore ( x in [ name_mappings[y] if y in name_mappings else y for y in channel_names ] ) if channel_names else True and uploader.check_channel_exists(channel_name=x) and not uploader.check_insert_finished( channel_name=x, old_members_dict=old_members_dict, old_channel_name=( reverse_name_mappings[x] if x in reverse_name_mappings else x ), ) ), conflicts, ) ) if len(conflicts) > 0 and not override: raise ValueError( f"channels: {', '.join(conflicts)} are already exist. please set mapping or override=True" ) uploader.create_all_channels( channel_names=channel_names, name_mappings=name_mappings ) channel_files: List[str] = glob.glob( os.path.join(uploader.local_data_dir, "channels", "*.json") ) if channel_names: channel_files = list( filter( lambda x: (os.path.basename(x)[:-5] in channel_names), # type: ignore channel_files, ) ) for i, channel_file_path in enumerate(channel_files): old_channel_name = os.path.basename(channel_file_path).replace(".json", "") if old_channel_name in name_mappings: new_channel_name = name_mappings[old_channel_name] else: new_channel_name = old_channel_name print( f"{i + 1}/{len(channel_files)}: {old_channel_name} -> {new_channel_name}" ) if ( uploader.check_channel_exists(channel_name=new_channel_name) and uploader.check_insert_finished( channel_name=new_channel_name, old_members_dict=old_members_dict, old_channel_name=old_channel_name, ) and not override ): print("already finished. skip.") continue new_channel_id = uploader.data_insert( channel_name=new_channel_name, old_members_dict=old_members_dict, old_members_icon_url_dict=old_members_icon_url_dict, old_channel_name=old_channel_name, ) if not skip_bookmarks: uploader.insert_bookmarks( channel_id=new_channel_id, old_channel_name=old_channel_name )
def set_parser_run(parser: argparse.ArgumentParser) -> None: parser.add_argument( "--data_dir", "-d", type=str, required=True, help="Data directory for saving download data or loading upload data. This is required.", ) parser.add_argument( "--downloader_token", "-td", type=str, default=None, help="Download token obtained from slack (the original workspace). Starts with `xoxb-`", ) parser.add_argument( "--uploader_token", "-tu", type=str, default=None, help="Upload token obtained from slack (the destination workspace). Starts with `xoxb-`", ) parser.add_argument( "--override", action="store_true", help="This flag enables message migration to the destination workspace even when a channel already exists." + " This means that additional messages are inserted after the message already sent to the channel." + " If you want not to do so, please delete the channel on the destination workspace first.", ) parser.add_argument( "--skip_download", action="store_true", help="Skip download. This is usually used when the download is already finished.", ) parser.add_argument( "--skip_upload", action="store_true", help="Skip upload. This is usually used when only the download is necessary.", ) parser.add_argument( "--channel_names", type=str, default=None, help="channel names you want to process. If not set, set to all available channels. " + "Set by comma-separation for multiple inputs. For example, `general,random`", ) parser.add_argument( "--name_mappings", type=str, default=None, help="You can set name mappings between the channel names of the original and destination workspaces. " + "Comma-separated dictionaries (key:value) are available. For example, `old_name1:new_name1,old_name2:new_name2`.", ) parser.add_argument( "--skip_bookmarks", action="store_true", help="Skip process bookmarks." ) def main_run(args: argparse.Namespace) -> None: name_mappings = None if args.name_mappings: name_mappings = dict( [ (dict_input.split(":")[0], dict_input.split(":")[1]) for dict_input in args.name_mappings.split(",") ] ) if args.channel_names is not None: channel_names = args.channel_names.split(",") else: channel_names = None if name_mappings: print(f"name mappings: {name_mappings}") run( local_data_dir=args.data_dir, downloader_token=args.downloader_token, uploader_token=args.uploader_token, override=args.override, skip_download=args.skip_download, skip_upload=args.skip_upload, channel_names=channel_names, name_mappings=name_mappings, skip_bookmarks=args.skip_bookmarks, ) if __name__ == "__main__": parser = argparse.ArgumentParser() set_parser_run(parser=parser) args = parser.parse_args() main_run(args=args)