2 |
3 | {{ room.title }}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018-2021 Cisco and/or its affiliates.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/webexteamsarchiver/static/.css/featherlight.min.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Featherlight - ultra slim jQuery lightbox
3 | * Version 1.7.13 - http://noelboss.github.io/featherlight/
4 | *
5 | * Copyright 2018, Noël Raoul Bossart (http://www.noelboss.com)
6 | * MIT Licensed.
7 | **/
8 | html.with-featherlight{overflow:hidden}.featherlight{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:2147483647;text-align:center;white-space:nowrap;cursor:pointer;background:#333;background:rgba(0,0,0,0)}.featherlight:last-of-type{background:rgba(0,0,0,.8)}.featherlight:before{content:'';display:inline-block;height:100%;vertical-align:middle}.featherlight .featherlight-content{position:relative;text-align:left;vertical-align:middle;display:inline-block;overflow:auto;padding:25px 25px 0;border-bottom:25px solid transparent;margin-left:5%;margin-right:5%;max-height:95%;background:#fff;cursor:auto;white-space:normal}.featherlight .featherlight-inner{display:block}.featherlight link.featherlight-inner,.featherlight script.featherlight-inner,.featherlight style.featherlight-inner{display:none}.featherlight .featherlight-close-icon{position:absolute;z-index:9999;top:0;right:0;line-height:25px;width:25px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;background:#fff;background:rgba(255,255,255,.3);color:#000;border:0;padding:0}.featherlight .featherlight-close-icon::-moz-focus-inner{border:0;padding:0}.featherlight .featherlight-image{width:100%}.featherlight-iframe .featherlight-content{border-bottom:0;padding:0;-webkit-overflow-scrolling:touch}.featherlight iframe{border:0}.featherlight *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@media only screen and (max-width:1024px){.featherlight .featherlight-content{margin-left:0;margin-right:0;max-height:98%;padding:10px 10px 0;border-bottom:10px solid transparent}}@media print{html.with-featherlight>*>:not(.featherlight){display:none}}
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import re
4 | from setuptools import setup, find_packages
5 |
6 | __copyright__ = "Copyright (c) 2018 Cisco and/or its affiliates."
7 | __license__ = "MIT"
8 |
9 |
10 | PACKAGE_NAME = 'webexteamsarchiver'
11 |
12 | PACKAGE_KEYWORDS = [
13 | 'cisco',
14 | 'webex',
15 | 'teams',
16 | 'spark',
17 | 'python',
18 | 'messaging',
19 | ]
20 |
21 | PACKAGE_CLASSIFIERS = [
22 | 'Development Status :: 4 - Beta',
23 | 'Intended Audience :: System Administrators',
24 | 'Intended Audience :: Telecommunications Industry',
25 | 'Intended Audience :: Information Technology',
26 | 'Natural Language :: English',
27 | 'License :: OSI Approved :: MIT License',
28 | 'Programming Language :: Python :: 3',
29 | 'Programming Language :: Python :: 3.5',
30 | 'Programming Language :: Python :: 3.6',
31 | 'Programming Language :: Python :: 3.7',
32 | 'Topic :: Communications',
33 | 'Topic :: Communications :: Chat'
34 | ]
35 |
36 | INSTALLATION_REQUIREMENTS = [
37 | 'webexteamssdk',
38 | 'jinja2',
39 | 'requests',
40 | 'hurry.filesize',
41 | 'bump2version',
42 | ]
43 |
44 | long_description = open(
45 | os.path.join(
46 | os.path.dirname(__file__),
47 | 'README.rst'
48 | )
49 | ).read()
50 |
51 | setup(
52 | name=PACKAGE_NAME,
53 | author='Felipe de Mello',
54 | author_email='fdemello@cisco.com',
55 | version='0.11.3',
56 | url='https://github.com/CiscoDevNet/webex-teams-archiver',
57 | description='Room archiver utility for Webex Teams',
58 | long_description=long_description,
59 | packages=find_packages('.'),
60 | include_package_data=True,
61 | install_requires=INSTALLATION_REQUIREMENTS,
62 | keywords=' '.join(PACKAGE_KEYWORDS),
63 | classifiers=PACKAGE_CLASSIFIERS,
64 | license='MIT; Copyright (c) 2018 Cisco Systems, Inc.'
65 | )
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # IPython
77 | profile_default/
78 | ipython_config.py
79 |
80 | # pyenv
81 | .python-version
82 |
83 | # celery beat schedule file
84 | celerybeat-schedule
85 |
86 | # SageMath parsed files
87 | *.sage.py
88 |
89 | # Environments
90 | .env
91 | .venv
92 | env/
93 | venv/
94 | ENV/
95 | env.bak/
96 | venv.bak/
97 |
98 | # Spyder project settings
99 | .spyderproject
100 | .spyproject
101 |
102 | # Rope project settings
103 | .ropeproject
104 |
105 | # mkdocs documentation
106 | /site
107 |
108 | # mypy
109 | .mypy_cache/
110 | .dmypy.json
111 | dmypy.json
112 |
113 | # Mac files
114 | .DS_Store
--------------------------------------------------------------------------------
/webexteamsarchiver/templates/default.html:
--------------------------------------------------------------------------------
1 |
2 | {% include "header.html" %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {% include "room_content.html" %}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 | =========
3 |
4 | 0.11.3: 2022-03-08
5 | -----------------
6 | Fixes:
7 | - Froze version for MarkupSafe in order to support Python 3.6
8 |
9 |
10 | 0.11.2: 2021-10-29
11 | -----------------
12 | Fixes:
13 | - Removed the use of context manager for requests since it's only available after v2.18.0
14 |
15 | 0.11.0: 2020-08-19
16 | -----------------
17 | New Features:
18 | - Added a initialization parameter for special tokens that have access to all messages in a space.
19 |
20 | 0.10.0: 2020-04-18
21 | -----------------
22 | New Features:
23 | - Support for threads.
24 | - Added a `compress_folder` option to allow users to choose whether or not to create the compressed archive.
25 | - Removed the `overwrite_folder` option since it had no effect.
26 |
27 | Fixes:
28 | - Text archives were missing a space between the e-mail and timestamp.
29 |
30 | 0.9.2: 2019-09-02
31 | -----------------
32 | Fixes:
33 | - Removed .DS_Store files from repo.
34 |
35 | 0.9.1: 2019-09-02
36 | -----------------
37 | New Features:
38 | - Added our own CSS and fonts to enable a true offline archive.
39 |
40 | 0.2.1: 2018-09-26
41 | -----------------
42 | Fixes:
43 | - Changed spacing and line length to almost comply with PEP8.
44 |
45 | 0.2.0: 2018-09-26
46 | -----------------
47 | Fixes:
48 | - More docstrings and type hints changes.
49 | - Moved datetime_format back to jinja_env to avoid changing global variable.
50 |
51 | New features:
52 | - Added logging support.
53 |
54 | 0.1.0: 2018-09-25
55 | -----------------
56 | Fixes:
57 | - Fixed docstrings and type hints.
58 | - Remove async from methods that did not need to be asynchronous.
59 | - Broken jinja template into multiple files to avoid the many levels of indentation.
60 | - Removed assertions in favor of type hints.
61 |
62 | New features:
63 | - Added support to compress folder on finish.
64 | - Added support for custom timestamp.
65 | - Replaced Webex Teams (unlicensed) CSS/fonts with collab-ui CSS/fonts (MIT).
66 |
67 | 0.0.1: 2018-09-24
68 | -----------------
69 | First commit.
70 |
--------------------------------------------------------------------------------
/webexteamsarchiver/templates/default.txt:
--------------------------------------------------------------------------------
1 | Title: {{ room.title }}
2 | Created by {{ room_creator.displayName }} ({{ room_creator.emails[0] }}) on {{ room.created|datetime_format(timestamp_format) }}{% if room.lastActivity %} and last had activity on {{ room.lastActivity|datetime_format(timestamp_format) }}{% endif %}.
3 |
4 | {% for msg in messages %}
5 | {% if not msg.parentId %}
6 | {{- msg.created|datetime_format(timestamp_format) }}{{" "}}{% if msg.personEmail %}{{ msg.personEmail }}{% else %}{{ people.get(msg.personId, {'emails': ['unknown']}).emails[0] }}{% endif %}: {{ msg.text|format_msg(False) }}
7 | {% if msg.files %}
8 | {% for url in msg.files %}
9 | {% if attachments[url].deleted == false %}
10 | {{- msg.created|datetime_format(timestamp_format) }}{{" "}}{% if msg.personEmail %}{{msg.personEmail}}{% else %}{{ people.get(msg.personId, {'emails': ['unknown']}).emails[0] }}{% endif %}: Attachment: {{ attachments[url].filename }} ({{ attachments[url].content_length }} bytes, {{ attachments[url].content_type }})
11 | {% else %}
12 | Attachment: File deleted or not found.
13 | {% endif %}
14 | {% endfor %}
15 | {% endif %}
16 | {% if msg.id in threads and threads[msg.id]|length > 0 %}
17 | {% for response in threads[msg.id] %}
18 | {{- " (reply) " }}{{- response.created|datetime_format(timestamp_format) }}{{" "}}{% if response.personEmail %}{{response.personEmail}}{% else %}{{ people[response.personId].emails[0] }}{% endif %}: {{ response.text|format_msg(True) }}
19 | {% if response.files %}
20 | {% for url in response.files %}
21 | {% if attachments[url].deleted == false %}
22 | {{- " (reply) " }}{{- response.created|datetime_format(timestamp_format) }}{{" "}}{% if response.personEmail %}{{response.personEmail}}{% else %}{{ people[response.personId].emails[0] }}{% endif %}: Attachment: {{ attachments[url].filename }} ({{ attachments[url].content_length }} bytes, {{ attachments[url].content_type }})
23 | {% else %}
24 | {{- " (reply) " }}Attachment: File deleted or not found.
25 | {% endif %}
26 | {% endfor %}
27 | {% endif %}
28 | {% endfor %}
29 | {% endif %}
30 | {% endif %}
31 | {% endfor %}
--------------------------------------------------------------------------------
/webexteamsarchiver/static/.css/teams.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Roboto', sans-serif !important;
3 | font-weight: 300 !important;
4 | }
5 |
6 | a {
7 | color: #05a1bf !important;
8 | -webkit-font-smoothing: antialiased;
9 | }
10 |
11 | h4 {
12 | font-weight: 300;
13 | }
14 |
15 | /* text */
16 |
17 | #activities .activity-item {
18 | display: flex;
19 | -ms-flex-align: start;
20 | align-items: flex-start;
21 | margin-top: 1em;
22 | }
23 |
24 | #activities .activity-item--message {
25 | font-size: 16px;
26 | display: inline-block;
27 | text-align: left;
28 | color: #000;
29 | word-wrap: break-word;
30 | white-space: pre-wrap;
31 | max-width: 800px;
32 | width: 100%;
33 | }
34 |
35 | .cui-button.cui-button--none {
36 | border-color: hsla(0, 0%, 90%, 0.88);
37 | border-radius: 50px;
38 | padding: 0;
39 | min-width: 0;
40 | margin-right: 17px;
41 | }
42 |
43 | .cui-avatar.cui-avatar--36 {
44 | width: 2.25rem;
45 | height: 2.25rem;
46 | font-size: 0.7875rem;
47 | line-height: 1.125rem;
48 | }
49 |
50 | .cui-avatar .cui-avatar__letter {
51 | display: flex;
52 | justify-content: center;
53 | align-items: center;
54 | width: 100%;
55 | height: 100%;
56 | border-radius: 50px;
57 | background-color: hsla(0, 0%, 90%, 0.88);
58 | }
59 |
60 | .cui-avatar .cui-avatar__img {
61 | border-radius: 50%;
62 | }
63 |
64 | .activity-item-sender-meta {
65 | font-size: 14px;
66 | color: #6b6b6b;
67 | margin-bottom: 0.2em;
68 | }
69 |
70 | /*
71 | .activity-item-sender-meta__publishedDate {
72 | margin-left: 16px;
73 | } */
74 |
75 | spark-mention {
76 | color: #969696;
77 | }
78 |
79 | #activities .activity-item .content {
80 | padding: 0px 4px;
81 | min-width: 250px !important;
82 | max-width: 90%;
83 | width: 100%;
84 | }
85 |
86 | .activity--reply {
87 | margin: 0 16px 0 68px;
88 | border-left: 4px solid rgba(0, 0, 0, 0.12);
89 | }
90 |
91 | .activity--reply .activity-item {
92 | padding: 10px 0 0;
93 | }
94 |
95 | .activity--reply .activity-item-left-child {
96 | margin: 6px 0 0 16px;
97 | flex: none;
98 | }
99 |
100 | /* ----------------- */
101 |
102 | /* Files-images */
103 |
104 | .shareList {
105 | list-style: none;
106 | margin: 10px 0;
107 | padding: 0;
108 | }
109 |
110 | img {
111 | max-width: 100%;
112 | }
113 |
114 | .mid-dot {
115 | margin-right: 10px;
116 | }
117 |
118 | .chip-info {
119 | margin-top: 15px;
120 | padding: 11px;
121 | background-color: #f5f5f5;
122 | font-size: 14px;
123 | border-radius: 5px;
124 | color: #6b6b6b;
125 | }
126 |
127 | .file-chip-secondary {
128 | display: flex;
129 | }
130 | .file-chip-meta-size,
131 | .file-chip-secondary .mid-dot {
132 | flex: 0 0 auto;
133 | }
134 |
135 | /* ---------------------- */
136 |
--------------------------------------------------------------------------------
/webexteamsarchiver/jinja_env.py:
--------------------------------------------------------------------------------
1 | """Jinja Configuration.
2 |
3 | Copyright (c) 2018-2021 Cisco and/or its affiliates.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 | """
21 | import jinja2
22 | import datetime
23 | import re
24 | from hurry.filesize import size, alternative
25 |
26 | __all__ = ['env', 'sanitize_name']
27 |
28 |
29 | def filesize_format(size_bytes):
30 | if not str(size_bytes).isdigit():
31 | return 0
32 |
33 | return size(int(size_bytes), system=alternative)
34 |
35 |
36 | def person_letters(display_name: str) -> str:
37 | if not display_name:
38 | return "Person Not Found"
39 |
40 | output = ""
41 | for name in display_name.upper().split():
42 | output += name[0]
43 |
44 | if len(output) > 2:
45 | return f"{output[0]}{output[-1]}"
46 |
47 | return output
48 |
49 |
50 | def datetime_format(date: datetime, format: str) -> str:
51 | if not date:
52 | return str(date)
53 |
54 | return date.strftime(format)
55 |
56 |
57 | def sanitize_name(text: str) -> str:
58 | text = str(text).strip().replace(' ', '_')
59 | return re.sub(r'(?u)[^-\w.]', '', text)
60 |
61 |
62 | def format_msg(text: str, thread: bool) -> str:
63 | spaces = " " * 2
64 | if thread:
65 | spaces *= 3
66 |
67 | if isinstance(text, str) and "\n" in text:
68 | text = re.sub("\\n", f"\\n{spaces}", text)
69 | text = f"{spaces}{text}"
70 | return f"\n{text}"
71 |
72 | return text
73 |
74 |
75 | env = jinja2.Environment(
76 | autoescape=False,
77 | trim_blocks=True,
78 | lstrip_blocks=True,
79 | keep_trailing_newline=True,
80 | loader=jinja2.PackageLoader('webexteamsarchiver', 'templates')
81 | )
82 |
83 | env.filters['filesize_format'] = filesize_format
84 | env.filters['person_letters'] = person_letters
85 | env.filters['datetime_format'] = datetime_format
86 | env.filters['sanitize_name'] = sanitize_name
87 | env.filters['format_msg'] = format_msg
88 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | =====================
2 | Webex Teams Archiver
3 | =====================
4 |
5 | *Simple utility to archive Webex Teams rooms*
6 |
7 | .. image:: https://static.production.devnetcloud.com/codeexchange/assets/images/devnet-published.svg
8 | :target: https://developer.cisco.com/codeexchange/github/repo/CiscoDevNet/webex-teams-archiver
9 | .. image:: https://img.shields.io/badge/license-MIT-blue.svg
10 | :target: https://github.com/CiscoDevNet/webex-teams-archiver/blob/master/LICENSE
11 | .. image:: https://img.shields.io/pypi/v/webexteamsarchiver.svg
12 | :target: https://pypi.python.org/pypi/webexteamsarchiver
13 |
14 | -------------------------------------------------------------------------------
15 |
16 | Webex Teams Archiver extracts the messages and files out of a Webex Teams room and saves them in text, HTML, and JSON formats.
17 |
18 | Example
19 | -------
20 |
21 | .. code-block:: python
22 |
23 | from webexteamsarchiver import WebexTeamsArchiver
24 |
25 | personal_token = "mytoken"
26 | archiver = WebexTeamsArchiver(personal_token)
27 |
28 | # room id from https://developer.webex.com/docs/api/v1/rooms/list-rooms
29 | room_id = "Y2lzY29zcGFyazovL3VzL1JPT00vd2ViZXh0ZWFtc2FyY2hpdmVy"
30 | archiver.archive_room(room_id)
31 |
32 | Produces the following files:
33 |
34 | .. code-block:: bash
35 |
36 | $ ls
37 | Title_Timestamp.tgz
38 | Title_Timestamp
39 |
40 | $ ls Title_Timestamp/
41 | Title_Timestamp.html
42 | Title_Timestamp.json
43 | Title_Timestamp.txt
44 | attachments/
45 | avatars/
46 | space_details.json
47 |
48 | Below is an example of a simple room that got archived.
49 |
50 | .. image:: https://raw.githubusercontent.com/CiscoDevNet/webex-teams-archiver/master/sample.png
51 | :scale: 40 %
52 |
53 |
54 | Note 1: The HTML version of the archive requires Internet connectivity because of the CSS, which is not packaged with the archive because of licensing conflicts.
55 |
56 | Note 2: Please note that use of the Webex Teams Archiver may violate the retention policy, if any, applicable to your use of Webex Teams.
57 |
58 | Installation
59 | ------------
60 |
61 | Installing and upgrading is easy:
62 |
63 | **Install via PIP**
64 |
65 | .. code-block:: bash
66 |
67 | $ pip install webexteamsarchiver
68 |
69 | **Upgrading to the latest Version**
70 |
71 | .. code-block:: bash
72 |
73 | $ pip install webexteamsarchiver --upgrade
74 |
75 | Options
76 | -------
77 |
78 | The `archive_room` method exposes the following options:
79 |
80 | +----------------------+-------------------+---------------------------------------------------+
81 | | Argument | Default Value | Description |
82 | +======================+===================+===================================================+
83 | | text_format | True | Create a text version of the archive |
84 | +----------------------+-------------------+---------------------------------------------------+
85 | | html_format | True | Create an HTML version of the archive |
86 | +----------------------+-------------------+---------------------------------------------------+
87 | | json_format | True | Create a JSON version of the archive |
88 | +----------------------+-------------------+---------------------------------------------------+
89 |
90 |
91 | In addition, the `options` kwargs supports the following additional options today:
92 |
93 | +----------------------+-------------------+---------------------------------------------------+
94 | | Argument | Default Value | Description |
95 | +======================+===================+===================================================+
96 | | compress_folder | True | Compress archive folder |
97 | +----------------------+-------------------+---------------------------------------------------+
98 | | delete_folder | False | Delete the archive folder when done |
99 | +----------------------+-------------------+---------------------------------------------------+
100 | | reverse_order | True | Order messages by most recent on the bottom |
101 | +----------------------+-------------------+---------------------------------------------------+
102 | | download_attachments | True | Download attachments sent to the room |
103 | +----------------------+-------------------+---------------------------------------------------+
104 | | download_avatars | True | Download avatar images |
105 | +----------------------+-------------------+---------------------------------------------------+
106 | | download_workers | 15 | Number of download workers for downloading files |
107 | +----------------------+-------------------+---------------------------------------------------+
108 | | timestamp_format | %Y-%m-%dT%H:%M:%S | Timestamp strftime format |
109 | +----------------------+-------------------+---------------------------------------------------+
110 | | file_format | gztar | Archive file format_ |
111 | +----------------------+-------------------+---------------------------------------------------+
112 |
113 | Questions, Support & Discussion
114 | -------------------------------
115 |
116 | webexteamsarchiver_ is a *community developed* and *community supported* project. Feedback, thoughts, questions, issues can be submitted using the issues_ page.
117 |
118 | Contribution
119 | ------------
120 |
121 | webexteamsarchiver_ is a *community developed* project. Code contributions are welcome via PRs!
122 |
123 | *Copyright (c) 2018-2021 Cisco and/or its affiliates.*
124 |
125 |
126 | .. _webexteamsarchiver: https://github.com/CiscoDevNet/webex-teams-archiver
127 | .. _issues: https://github.com/CiscoDevNet/webex-teams-archiver/issues
128 | .. _format: https://docs.python.org/3/library/shutil.html#shutil.make_archive
129 |
--------------------------------------------------------------------------------
/webexteamsarchiver/static/.js/featherlight.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Featherlight - ultra slim jQuery lightbox
3 | * Version 1.7.13 - http://noelboss.github.io/featherlight/
4 | *
5 | * Copyright 2018, Noël Raoul Bossart (http://www.noelboss.com)
6 | * MIT Licensed.
7 | **/
8 | !function(a){"use strict";function b(a,c){if(!(this instanceof b)){var d=new b(a,c);return d.open(),d}this.id=b.id++,this.setup(a,c),this.chainCallbacks(b._callbackChain)}function c(a,b){var c={};for(var d in a)d in b&&(c[d]=a[d],delete a[d]);return c}function d(a,b){var c={},d=new RegExp("^"+b+"([A-Z])(.*)");for(var e in a){var f=e.match(d);if(f){var g=(f[1]+f[2].replace(/([A-Z])/g,"-$1")).toLowerCase();c[g]=a[e]}}return c}if("undefined"==typeof a)return void("console"in window&&window.console.info("Too much lightness, Featherlight needs jQuery."));if(a.fn.jquery.match(/-ajax/))return void("console"in window&&window.console.info("Featherlight needs regular jQuery, not the slim version."));var e=[],f=function(b){return e=a.grep(e,function(a){return a!==b&&a.$instance.closest("body").length>0})},g={allow:1,allowfullscreen:1,frameborder:1,height:1,longdesc:1,marginheight:1,marginwidth:1,mozallowfullscreen:1,name:1,referrerpolicy:1,sandbox:1,scrolling:1,src:1,srcdoc:1,style:1,webkitallowfullscreen:1,width:1},h={keyup:"onKeyUp",resize:"onResize"},i=function(c){a.each(b.opened().reverse(),function(){return c.isDefaultPrevented()||!1!==this[h[c.type]](c)?void 0:(c.preventDefault(),c.stopPropagation(),!1)})},j=function(c){if(c!==b._globalHandlerInstalled){b._globalHandlerInstalled=c;var d=a.map(h,function(a,c){return c+"."+b.prototype.namespace}).join(" ");a(window)[c?"on":"off"](d,i)}};b.prototype={constructor:b,namespace:"featherlight",targetAttr:"data-featherlight",variant:null,resetCss:!1,background:null,openTrigger:"click",closeTrigger:"click",filter:null,root:"body",openSpeed:250,closeSpeed:250,closeOnClick:"background",closeOnEsc:!0,closeIcon:"✕",loading:"",persist:!1,otherClose:null,beforeOpen:a.noop,beforeContent:a.noop,beforeClose:a.noop,afterOpen:a.noop,afterContent:a.noop,afterClose:a.noop,onKeyUp:a.noop,onResize:a.noop,type:null,contentFilters:["jquery","image","html","ajax","iframe","text"],setup:function(b,c){"object"!=typeof b||b instanceof a!=!1||c||(c=b,b=void 0);var d=a.extend(this,c,{target:b}),e=d.resetCss?d.namespace+"-reset":d.namespace,f=a(d.background||['
','
','",'
'+d.loading+"
","
","
"].join("")),g="."+d.namespace+"-close"+(d.otherClose?","+d.otherClose:"");return d.$instance=f.clone().addClass(d.variant),d.$instance.on(d.closeTrigger+"."+d.namespace,function(b){if(!b.isDefaultPrevented()){var c=a(b.target);("background"===d.closeOnClick&&c.is("."+d.namespace)||"anywhere"===d.closeOnClick||c.closest(g).length)&&(d.close(b),b.preventDefault())}}),this},getContent:function(){if(this.persist!==!1&&this.$content)return this.$content;var b=this,c=this.constructor.contentFilters,d=function(a){return b.$currentTarget&&b.$currentTarget.attr(a)},e=d(b.targetAttr),f=b.target||e||"",g=c[b.type];if(!g&&f in c&&(g=c[f],f=b.target&&e),f=f||d("href")||"",!g)for(var h in c)b[h]&&(g=c[h],f=b[h]);if(!g){var i=f;if(f=null,a.each(b.contentFilters,function(){return g=c[this],g.test&&(f=g.test(i)),!f&&g.regex&&i.match&&i.match(g.regex)&&(f=i),!f}),!f)return"console"in window&&window.console.error("Featherlight: no content filter found "+(i?' for "'+i+'"':" (no target specified)")),!1}return g.process.call(b,f)},setContent:function(b){return this.$instance.removeClass(this.namespace+"-loading"),this.$instance.toggleClass(this.namespace+"-iframe",b.is("iframe")),this.$instance.find("."+this.namespace+"-inner").not(b).slice(1).remove().end().replaceWith(a.contains(this.$instance[0],b[0])?"":b),this.$content=b.addClass(this.namespace+"-inner"),this},open:function(b){var c=this;if(c.$instance.hide().appendTo(c.root),!(b&&b.isDefaultPrevented()||c.beforeOpen(b)===!1)){b&&b.preventDefault();var d=c.getContent();if(d)return e.push(c),j(!0),c.$instance.fadeIn(c.openSpeed),c.beforeContent(b),a.when(d).always(function(a){c.setContent(a),c.afterContent(b)}).then(c.$instance.promise()).done(function(){c.afterOpen(b)})}return c.$instance.detach(),a.Deferred().reject().promise()},close:function(b){var c=this,d=a.Deferred();return c.beforeClose(b)===!1?d.reject():(0===f(c).length&&j(!1),c.$instance.fadeOut(c.closeSpeed,function(){c.$instance.detach(),c.afterClose(b),d.resolve()})),d.promise()},resize:function(a,b){if(a&&b){this.$content.css("width","").css("height","");var c=Math.max(a/(this.$content.parent().width()-1),b/(this.$content.parent().height()-1));c>1&&(c=b/Math.floor(b/c),this.$content.css("width",""+a/c+"px").css("height",""+b/c+"px"))}},chainCallbacks:function(b){for(var c in b)this[c]=a.proxy(b[c],this,a.proxy(this[c],this))}},a.extend(b,{id:0,autoBind:"[data-featherlight]",defaults:b.prototype,contentFilters:{jquery:{regex:/^[#.]\w/,test:function(b){return b instanceof a&&b},process:function(b){return this.persist!==!1?a(b):a(b).clone(!0)}},image:{regex:/\.(png|jpg|jpeg|gif|tiff?|bmp|svg)(\?\S*)?$/i,process:function(b){var c=this,d=a.Deferred(),e=new Image,f=a('');return e.onload=function(){f.naturalWidth=e.width,f.naturalHeight=e.height,d.resolve(f)},e.onerror=function(){d.reject(f)},e.src=b,d.promise()}},html:{regex:/^\s*<[\w!][^<]*>/,process:function(b){return a(b)}},ajax:{regex:/./,process:function(b){var c=a.Deferred(),d=a("").load(b,function(a,b){"error"!==b&&c.resolve(d.contents()),c.fail()});return c.promise()}},iframe:{process:function(b){var e=new a.Deferred,f=a(""),h=d(this,"iframe"),i=c(h,g);return f.hide().attr("src",b).attr(i).css(h).on("load",function(){e.resolve(f.show())}).appendTo(this.$instance.find("."+this.namespace+"-content")),e.promise()}},text:{process:function(b){return a("
",{text:b})}}},functionAttributes:["beforeOpen","afterOpen","beforeContent","afterContent","beforeClose","afterClose"],readElementConfig:function(b,c){var d=this,e=new RegExp("^data-"+c+"-(.*)"),f={};return b&&b.attributes&&a.each(b.attributes,function(){var b=this.name.match(e);if(b){var c=this.value,g=a.camelCase(b[1]);if(a.inArray(g,d.functionAttributes)>=0)c=new Function(c);else try{c=JSON.parse(c)}catch(h){}f[g]=c}}),f},extend:function(b,c){var d=function(){this.constructor=b};return d.prototype=this.prototype,b.prototype=new d,b.__super__=this.prototype,a.extend(b,this,c),b.defaults=b.prototype,b},attach:function(b,c,d){var e=this;"object"!=typeof c||c instanceof a!=!1||d||(d=c,c=void 0),d=a.extend({},d);var f,g=d.namespace||e.defaults.namespace,h=a.extend({},e.defaults,e.readElementConfig(b[0],g),d),i=function(g){var i=a(g.currentTarget),j=a.extend({$source:b,$currentTarget:i},e.readElementConfig(b[0],h.namespace),e.readElementConfig(g.currentTarget,h.namespace),d),k=f||i.data("featherlight-persisted")||new e(c,j);"shared"===k.persist?f=k:k.persist!==!1&&i.data("featherlight-persisted",k),j.$currentTarget.blur&&j.$currentTarget.blur(),k.open(g)};return b.on(h.openTrigger+"."+h.namespace,h.filter,i),{filter:h.filter,handler:i}},current:function(){var a=this.opened();return a[a.length-1]||null},opened:function(){var b=this;return f(),a.grep(e,function(a){return a instanceof b})},close:function(a){var b=this.current();return b?b.close(a):void 0},_onReady:function(){var b=this;if(b.autoBind){var c=a(b.autoBind);c.each(function(){b.attach(a(this))}),a(document).on("click",b.autoBind,function(d){if(!d.isDefaultPrevented()){var e=a(d.currentTarget),f=c.length;if(c=c.add(e),f!==c.length){var g=b.attach(e);(!g.filter||a(d.target).parentsUntil(e,g.filter).length>0)&&g.handler(d)}}})}},_callbackChain:{onKeyUp:function(b,c){return 27===c.keyCode?(this.closeOnEsc&&a.featherlight.close(c),!1):b(c)},beforeOpen:function(b,c){return a(document.documentElement).addClass("with-featherlight"),this._previouslyActive=document.activeElement,this._$previouslyTabbable=a("a, input, select, textarea, iframe, button, iframe, [contentEditable=true]").not("[tabindex]").not(this.$instance.find("button")),this._$previouslyWithTabIndex=a("[tabindex]").not('[tabindex="-1"]'),this._previousWithTabIndices=this._$previouslyWithTabIndex.map(function(b,c){return a(c).attr("tabindex")}),this._$previouslyWithTabIndex.add(this._$previouslyTabbable).attr("tabindex",-1),document.activeElement.blur&&document.activeElement.blur(),b(c)},afterClose:function(c,d){var e=c(d),f=this;return this._$previouslyTabbable.removeAttr("tabindex"),this._$previouslyWithTabIndex.each(function(b,c){a(c).attr("tabindex",f._previousWithTabIndices[b])}),this._previouslyActive.focus(),0===b.opened().length&&a(document.documentElement).removeClass("with-featherlight"),e},onResize:function(a,b){return this.resize(this.$content.naturalWidth,this.$content.naturalHeight),a(b)},afterContent:function(a,b){var c=a(b);return this.$instance.find("[autofocus]:not([disabled])").focus(),this.onResize(b),c}}}),a.featherlight=b,a.fn.featherlight=function(a,c){return b.attach(this,a,c),this},a(document).ready(function(){b._onReady()})}(jQuery);
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "c97e532400bdb6feb5a99afff98cb5ed066ba8fbeda44727f06106313adc7c5b"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.9"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "bump2version": {
20 | "hashes": [
21 | "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410",
22 | "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"
23 | ],
24 | "index": "pypi",
25 | "version": "==1.0.1"
26 | },
27 | "certifi": {
28 | "hashes": [
29 | "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
30 | "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
31 | ],
32 | "version": "==2021.10.8"
33 | },
34 | "charset-normalizer": {
35 | "hashes": [
36 | "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
37 | "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
38 | ],
39 | "markers": "python_version >= '3'",
40 | "version": "==2.0.12"
41 | },
42 | "future": {
43 | "hashes": [
44 | "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
45 | ],
46 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
47 | "version": "==0.18.2"
48 | },
49 | "hurry.filesize": {
50 | "hashes": [
51 | "sha256:f5368329adbef86accd3bc9490522340bb79260455ae89b1a42c10f63801b9a6"
52 | ],
53 | "index": "pypi",
54 | "version": "==0.9"
55 | },
56 | "idna": {
57 | "hashes": [
58 | "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
59 | "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
60 | ],
61 | "markers": "python_version >= '3'",
62 | "version": "==3.3"
63 | },
64 | "jinja2": {
65 | "hashes": [
66 | "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
67 | "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
68 | ],
69 | "index": "pypi",
70 | "version": "==2.11.3"
71 | },
72 | "markupsafe": {
73 | "hashes": [
74 | "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298",
75 | "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64",
76 | "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b",
77 | "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194",
78 | "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567",
79 | "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff",
80 | "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724",
81 | "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74",
82 | "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646",
83 | "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35",
84 | "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6",
85 | "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a",
86 | "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6",
87 | "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad",
88 | "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26",
89 | "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38",
90 | "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac",
91 | "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7",
92 | "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6",
93 | "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047",
94 | "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75",
95 | "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f",
96 | "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b",
97 | "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135",
98 | "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8",
99 | "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a",
100 | "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a",
101 | "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1",
102 | "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9",
103 | "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864",
104 | "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914",
105 | "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee",
106 | "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f",
107 | "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18",
108 | "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8",
109 | "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2",
110 | "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d",
111 | "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b",
112 | "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b",
113 | "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86",
114 | "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6",
115 | "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f",
116 | "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb",
117 | "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833",
118 | "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28",
119 | "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e",
120 | "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415",
121 | "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902",
122 | "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f",
123 | "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d",
124 | "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9",
125 | "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d",
126 | "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145",
127 | "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066",
128 | "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c",
129 | "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1",
130 | "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a",
131 | "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207",
132 | "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f",
133 | "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53",
134 | "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd",
135 | "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134",
136 | "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85",
137 | "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9",
138 | "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5",
139 | "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94",
140 | "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509",
141 | "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51",
142 | "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"
143 | ],
144 | "index": "pypi",
145 | "version": "==2.0.1"
146 | },
147 | "pyjwt": {
148 | "hashes": [
149 | "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41",
150 | "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"
151 | ],
152 | "markers": "python_version >= '3.6'",
153 | "version": "==2.3.0"
154 | },
155 | "requests": {
156 | "hashes": [
157 | "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
158 | "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
159 | ],
160 | "index": "pypi",
161 | "version": "==2.27.1"
162 | },
163 | "requests-toolbelt": {
164 | "hashes": [
165 | "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f",
166 | "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"
167 | ],
168 | "version": "==0.9.1"
169 | },
170 | "setuptools": {
171 | "hashes": [
172 | "sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5",
173 | "sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b"
174 | ],
175 | "markers": "python_version >= '3.7'",
176 | "version": "==60.9.3"
177 | },
178 | "urllib3": {
179 | "hashes": [
180 | "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
181 | "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
182 | ],
183 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
184 | "version": "==1.26.8"
185 | },
186 | "webexteamssdk": {
187 | "hashes": [
188 | "sha256:980f268d89187d1a157dfbadcb626fce849080a31550e549cfe838f0203b3a3d",
189 | "sha256:e4958dca3903c659fe1e0f79e38740cb2ec1ae2589599807b179c3efb356c3a2"
190 | ],
191 | "index": "pypi",
192 | "version": "==1.6"
193 | }
194 | },
195 | "develop": {}
196 | }
197 |
--------------------------------------------------------------------------------
/webexteamsarchiver/static/.fonts/Roboto/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/webexteamsarchiver/templates/room_content.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ room.title|e }}
3 | Created by {{ room_creator.displayName }} on {{ room.created|datetime_format(timestamp_format) }}{% if room.lastActivity %} and last had activity on {{ room.lastActivity|datetime_format(timestamp_format)
4 | }}{% endif %}.
5 |
6 |
7 |
8 | {% for msg in messages %}
9 | {% if not msg.parentId %}
10 |