将邮件列表迁移到 Discourse(mbox、Listserv、Google Groups 等)」

This guide is for you if you want to migrate a mailing list to Discourse.
It also contains instructions for importing messages from image Google Groups.

1. Importing using Docker container

This is the recommended way for importing content from your mailing lists into Discourse.

1.1. Installing Discourse

:bulb: The import script most likely won’t work on systems with less than 4GB of RAM. Recommended are 8GB of RAM or more. You can scale back the RAM usage after the import if you like.

Install Discourse by following the official installation guide. Afterwards it’s a good idea to go to the Admin section and configure a few settings:

  • Enable login_required if imported topics shouldn’t be visible to the public

  • Enable hide_user_profiles_from_public if user profiles shouldn’t be visible to the public.

  • Disable download_remote_images_to_local if you don’t want Discourse to download images embedded in posts.

  • Enable disable_edit_notifications if you enabled download_remote_images_to_local and don’t want your users to get lots of notifications about posts edited by the system user.

  • Change the value of slug_generation_method if most of the topic titles use characters which shouldn’t be mapped to ASCII (e.g. Arabic). See this post for more information.

:bangbang: The following steps assume that you installed Discourse on Ubuntu and that you are connected to the machine via SSH or have direct access to the machine’s terminal.

1.2. Preparing the Docker container

Copy the container configuration file app.yml to import.yml and edit it with your favorite editor.

cd /var/discourse
cp containers/app.yml containers/import.yml
nano containers/import.yml
Regular import

Add - "templates/import/mbox.template.yml" to the list of templates. Afterwards it should look something like this:

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"
  - "templates/import/mbox.template.yml"

That’s it. You can save the file, close the editor and build the container.

Google Groups import

You need to add two entries to the list of templates:

  - "templates/import/chrome-dep.template.yml"
  - "templates/import/mbox.template.yml"

Afterwards it should look something like this:

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"
  - "templates/import/chrome-dep.template.yml"
  - "templates/import/mbox.template.yml"

That’s it. You can save the file, close the editor and build the container.

/var/discourse/launcher stop app
/var/discourse/launcher rebuild import

Building the container creates an import directory within the container’s shared directory. It looks like this:

/var/discourse/shared/standalone/import
├── data
└── settings.yml

1.3. Downloading messages from Google Groups (optional)

You can skip this step unless you want to migrate from image Google Groups.

Instructions for Google Groups

1.3.1. Preparation

:warning: Make sure you don’t have any pinned posts in your group, otherwise the crawler might fail to download some or all messages.

:warning: Make sure the group settings allow posting, otherwise you might see “Failed to scrape message” error messages. It might take a couple of minutes before the scraping works when you changed those settings recently.

Google account: You need a Google account that has the Manager or Owner role for your Google Group, otherwise the downloaded messages will contain censored email addresses.

Group name: You can find the group name by visiting your Google Group and looking at the browser’s address bar. image

Domain name: The URL might look a little bit differently if you are a G Suite customer. You need to know the domain name if the URL contains something like example.com. image

1.3.2 Cookies :cookie:

In order to download messages, the crawler needs to have access to a Google account that has the owner role for your group. Please visit https://myaccount.google.com/ in your browser and sign in if you aren’t already logged in. Then use a browser extension of your choice to export your cookies for google.com in a file named cookies.txt.

The recommended browser extensions is Export Cookies for Mozilla Firefox.

Upload the cookies.txt file to your server and save it within the /var/discourse/shared/standalone/import directory.

1.3.3. Download messages

:bulb: Tip: It’s a good idea to download messages inside a tmux or screen session, so that you can reconnect to the session in case of SSH connection loss.

Let’s start by entering the Docker container.

/var/discourse/launcher enter import

Replace the <group_name> (and if applicable, the <domain_name>) placeholders within the following command with the group name and domain name from step 1.3.1 and execute it inside the Docker container in order to start the download of messages.

If you didn’t find a domain name in step 1.3.1, this is the command for you:

script/import_scripts/google_groups.rb -g <group_name>

Or, if you found a domain name in step 1.3.1, use this command instead:

script/import_scripts/google_groups.rb -g <group_name> -d <domain_name>

Downloading all messages can take a long time. It mostly depends on the number of topics in your Google Group. The script will show you a message like this when it’s finished: Done (00h 26min 52sec)

:bulb: Tip: You can abort the download anytime you want by pressing Ctrl+C
When you restart the download it will continue where it left off.

1.4. Configuring the importer

You can configure the importer by editing the example settings.yml file that has been copied into the import directory.

nano /var/discourse/shared/standalone/import/settings.yml

The settings file comes with sensible defaults, but here are a few tips anyway:

  • The settings file contains multiple examples on how to split data files:

    • mbox files usually are separated by a From header. Choose a regular expression that works for your files.

    • If each of your files contains only one message, set the split_regex to an empty string. This also applies to imports from image Google Groups.

    • There’s also an example for files from the popular Listserv mailing list software.

  • prefer_html allows you to configure if the import should use the HTML part of emails when it exists. You should choose what suits you best – it heavily depends on the emails sent to your mailing list.

  • By default each user imported from the mailing list is created as staged user. You can disable that behaviour by setting staged to false.

  • If your emails do not contain a Message-ID header (like messages stored by Listserv), you should enable the group_messages_by_subject setting.

1.5. Prepare files

Each subdirectory of /var/discourse/shared/standalone/import/data gets imported as its own category and each directory should contain the data files you want to import. The file names of those do not matter.

Example: The import directory should look like this if you want to import two mailing lists with multiple mbox files:

/var/discourse/shared/standalone/import
├── data
│   ├── list 1
│   │   ├── foo
│   │   ├── bar
│   ├── list 2
│   │   ├── 2017-12.mbox
│   │   ├── 2018-01.mbox
└── settings.yml

1.6. Executing the import script

:bulb: Tip: It’s a good idea to start the import inside a tmux or screen session, so that you can reconnect to the session in case of SSH connection loss.

Let’s start the import by entering the Docker container and launching the import script inside the Docker container.

/var/discourse/launcher enter import
import_mbox.sh # inside the Docker container

Depending on the size of your mailing lists it’s now time for some :coffee: or :sleeping:
The import script will show you a message like this when it’s finished: Done (00h 26min 52sec)

:bulb: Tip: You can abort the import anytime you want by pressing Ctrl+C
When you restart the import it will continue where it left off.

You can exit and stop the Docker container after the import has finished.

exit # inside the Docker container
/var/discourse/launcher stop import

1.7. Starting Discourse

Let’s start the app container and take a look at the imported data.

/var/discourse/launcher start app

Discourse will start and Sidekiq will begin post-processing all the imported posts. This can take a considerate amount of time. You can watch the progress by logging in as admin and visiting http://discourse.example.com/sidekiq

1.8. Clean up

So, you are satisfied with the result of the import and want to free some disk space? The following commands will delete the Docker container used for importing as well as all the files used during the import.

/var/discourse/launcher destroy import
rm /var/discourse/containers/import.yml
rm -R /var/discourse/shared/standalone/import

1.9. The End

Now it’s time to celebrate and enjoy your new Discourse instance! :tada:

2. FAQ

2.1. How can I remove list names (e.g. [Foo]) from topic titles during the import?

You can use an empty tag to remove one or more prefixes from topic titles. The settings file contains an example.

2.2 How can I prevent the import script from detecting messages as already being imported?

:warning: The following steps will reset your Discourse forum to the initial state! You will need to start from scratch.

The following commands will stop the container, delete everything except the mbox files and the importer configuration and restart the container.

Commands
cd /var/discourse

./launcher stop app
./launcher stop import

rm -r ./shared/standalone/!(import)
rm ./shared/standalone/import/data/index.db

./launcher rebuild import

./launcher enter import
import_mbox.sh # inside the Docker container

2.3 How can I manipulate messages before they are imported into Discourse?

Enable index_only in settings.yml and take a look at the index.db (a SQLite database) before you run the actual import.

You can use SQL to update missing values in the database if you want. That way you don’t need to reindex any messages. The script uses only data from the index.db during the import phase. Simply disable the index_only option when you are done and rerun the importer. It will skip the indexing if none of the mbox files were changed, recalculate the content of the user and email_order tables and start the actual import process.

2.4 How can I find messages which cause problems during the import?

You can split mbox files into individual files to make it easier to find offending emails.

Commands
apt install procmail;
export FILENO=0000;
formail -ds sh -c 'cat &gt; split/msg.$FILENO' < mbox;

2.5 I have already imported a group. How can I import another group?

Create a new directory in the import/data directory and restart the import script.

2.6 I don’t have access to Mailman archives in mbox format? Is there any other way to get them?

You could give this script a try.

Last edited by @JammyDodger 2024-05-27T14:56:11Z

Check documentPerform check on document:
30 个赞

@gerhard - I was able to migrate an mbox archive of 22,000 messages using this script on a Digital Ocean droplet with only 1GB RAM. No problems. Thank you for the write-up of instructions. Everything worked great. The only mistake I made on my first attempt was trying to name the /var/discourse/shared/standalone/import/data/X subfolder using a new category I created before running the script. That caused the import to place these messages into the Uncategorized category. On second attempt, I deleted the new category and tried again. This created the category name for me and placed the messages into the proper category automatically.

6 个赞

感谢这份指南。

我尝试导入 Google Groups 数据。不幸的是,运行 import_mbox.sh 时遇到了以下错误:

正在开始 mbox 导入...

Traceback (most recent call last):
5: from script/import_scripts/mbox.rb:9:in `<main>'
4: from script/import_scripts/mbox.rb:10:in `<module:ImportScripts>'
3: from script/import_scripts/mbox.rb:13:in `<module:Mbox>'
2: from /var/www/discourse/script/import_scripts/mbox/support/settings.rb:9:in `load'
1: from /var/www/discourse/script/import_scripts/mbox/support/settings.rb:9:in `new'

/var/www/discourse/script/import_scripts/mbox/support/settings.rb:42:in `initialize': undefined method `each' for nil:NilClass (NoMethodError)

不过,/var/discourse/shared/standalone/import/data/Foo 目录下的所有文件都是 .eml 格式,而不是 mbox 格式。这会有影响吗?

谢谢!

导入脚本的最新版本已修复该问题。作为替代方案,请更新您的 设置文件。最近进行了一些更改。

5 个赞

非常感谢。能否请您就如何更新导入脚本提供一些建议?

是否只需更新导入脚本即可,还是我需要重新执行指南中的更多步骤(具体是哪些步骤)?我找不到这些步骤,因此不知道该如何更新它们。

我已经按照您的建议更新了设置文件,但问题依然存在。

谢谢。

您可以运行 /var/discourse/launcher rebuild import 来更新导入脚本及其所有相关组件。

4 个赞

谢谢。

运行 import_mbox.sh 时,几乎所有消息都被跳过,并出现类似以下错误:

script/import_scripts/mbox.rb:12:in `<module:Mbox>'

script/import_scripts/mbox.rb:10:in `<module:ImportScripts>'

script/import_scripts/mbox.rb:9:in `<main>'

41 / 215 ( 19.1%) [59096 项/分钟] 无法为 36a37072-e5b6-4009-878f-f0824e40eac6@googlegroups.com 映射帖子

undefined method `each' for nil:NilClass

/var/www/discourse/script/import_scripts/mbox/importer.rb:179:in `block in remove_tags!'

/var/www/discourse/script/import_scripts/mbox/importer.rb:176:in `loop'

/var/www/discourse/script/import_scripts/mbox/importer.rb:176:in `remove_tags!'

/var/www/discourse/script/import_scripts/mbox/importer.rb:150:in `map_first_post'

/var/www/discourse/script/import_scripts/mbox/importer.rb:104:in `block (2 levels) in import_posts'

/var/www/discourse/script/import_scripts/base.rb:503:in `block in create_posts'

/var/www/discourse/script/import_scripts/base.rb:502:in `each'

/var/www/discourse/script/import_scripts/base.rb:502:in `create_posts'

/var/www/discourse/script/import_scripts/mbox/importer.rb:98:in `block in import_posts'

/var/www/discourse/script/import_scripts/base.rb:882:in `block in batches'

/var/www/discourse/script/import_scripts/base.rb:881:in `loop'

/var/www/discourse/script/import_scripts/base.rb:881:in `batches'

/var/www/discourse/script/import_scripts/mbox/importer.rb:84:in `batches'

/var/www/discourse/script/import_scripts/mbox/importer.rb:92:in `import_posts'

/var/www/discourse/script/import_scripts/mbox/importer.rb:36:in `execute'

/var/www/discourse/script/import_scripts/base.rb:47:in `perform'

此外还有:

60 / 215 ( 27.9%) [58321 项/分钟] 父消息 1b46f337-95a3-4b4a-a14a-689636941580@googlegroups.com 不存在。跳过 5634208e-e6df-4bd8-b361-0735f73fe554@googlegroups.com:

造成此问题的可能原因是什么?谢谢。

问题应已修复。请再次重新构建您的导入容器。

6 个赞

太棒了,效果出奇的好。:pray: 非常感谢您的支持。

5 个赞

我尝试下载 Google Groups 时遇到了以下错误:

登录失败。请检查您的 cookies.txt 文件内容

我使用了推荐的 Firefox 扩展来下载 cookies,昨天和今天各操作了一次。我已通过将其重命名为错误文件名并收到“未找到”错误,确认程序正在读取该文件。我下载了所有 cookies,而不仅仅是 Google 相关的。我还已退出并重新登录,再次下载了 cookies。

我可以看到自己是管理员,因为我拥有“管理群组”的选项。

我已再三确认,通过复制粘贴并检查格式,使用的是正确的群组名称,而非域名格式。

是系统出了问题,还是只有我遇到这个问题?

@gerhard,抱歉特意点名,您有什么快速建议可以帮助调试吗?也许登录接口已更改?

编辑:找到了问题。我稍后会提交一个 PR。登录接口已更改,我成功推测出了新的接口。:slight_smile:

1 个赞

新手尝试从 Yahoo 群组导入 mbox 文件。我已多次按照这些说明操作,但始终出现相同的错误信息。我看到其他人已成功导入,所以这很可能是一个新手常犯的错误。错误提示似乎表明 split_regex: "^From .+@.+" 未能找到用于分割文件的邮件键,但我在文本编辑器中测试过该正则表达式,它按预期工作。导入文件的第 2 行类似于 Message-ID: <35690.0.1.959300741@eGroups.com>

有任何建议吗?先谢过…

正在开始 mbox 导入...

Traceback (most recent call last):
	12: from script/import_scripts/mbox.rb:9:in `<main>'
	11: from script/import_scripts/mbox.rb:10:in `<module:ImportScripts>'
	10: from script/import_scripts/mbox.rb:12:in `<module:Mbox>'
	 9: from script/import_scripts/mbox.rb:12:in `new'
	 8: from /var/www/discourse/script/import_scripts/mbox/importer.rb:11:in `initialize'
	 7: from /var/www/discourse/script/import_scripts/mbox/support/settings.rb:8:in `load'
	 6: from /usr/local/lib/ruby/2.6.0/psych.rb:577:in `load_file'
	 5: from /usr/local/lib/ruby/2.6.0/psych.rb:577:in `open'
	 4: from /usr/local/lib/ruby/2.6.0/psych.rb:578:in `block in load_file'
	 3: from /usr/local/lib/ruby/2.6.0/psych.rb:277:in `load'
	 2: from /usr/local/lib/ruby/2.6.0/psych.rb:390:in `parse'
	 1: from /usr/local/lib/ruby/2.6.0/psych.rb:456:in `parse_stream'
/usr/local/lib/ruby/2.6.0/psych.rb:456:in `parse': (/shared/import/settings.yml): did not find expected key while parsing a block mapping at line 2 column 1 (Psych::SyntaxError)

看起来您在 settings.yml 文件中出现了错误。建议您访问 http://www.yamllint.com/ 验证配置。

3 个赞

感谢 @gerhard。唉……我本该早点发现这个问题的,这是我第一次接触 Ruby。现在,我觉得离解决问题更近了一步,但出现了另一个错误(见下文)。由于导入脚本现在正在加载群组等内容,我推测新错误已经过了最初的问题阶段。我还推测,所引用的数据库文件是由导入脚本创建的 import/index.db(但并未创建)。

正在开始 mbox 导入...

正在加载现有群组...
正在加载现有用户...
正在加载现有分类...
正在加载现有帖子...
正在加载现有主题...
回溯(最近一次调用在最后):
	9: 来自 script/import_scripts/mbox.rb:9:in `<main>'
	8: 来自 script/import_scripts/mbox.rb:10:in `<module:ImportScripts>'
	7: 来自 script/import_scripts/mbox.rb:12:in `<module:Mbox>'
	6: 来自 script/import_scripts/mbox.rb:12:in `new'
	5: 来自 /var/www/discourse/script/import_scripts/mbox/importer.rb:14:in `initialize'
	4: 来自 /var/www/discourse/script/import_scripts/mbox/importer.rb:14:in `new'
	3: 来自 /var/www/discourse/script/import_scripts/mbox/support/database.rb:10:in `initialize'
	2: 来自 /var/www/discourse/script/import_scripts/mbox/support/database.rb:10:in `new'
	1: 来自 /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/sqlite3-1.4.2/lib/sqlite3/database.rb:89:in `initialize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/sqlite3-1.4.2/lib/sqlite3/database.rb:89:in `open_v2': 无法打开数据库文件 (SQLite3::CantOpenException)
1 个赞

SYSTEM 不允许我编辑我的评论,所以我提交这条回复代替。

编辑:为了闭环……我的 Yahoo 群组导入现在可以工作了,至少已经索引了 9951 封邮件。我尚未完成完整导入,后续还会有更多进展。我多次修改了 settings.yml,现在已恢复为原始版本,它突然又能正常工作了!不再出现语法错误。我不明白为什么会出现许多在我看来不一致的错误消息。settings.yml 中最初的语法错误再次成了谜。上面的错误消息对我来说毫无意义……唉。

1 个赞

@gerhard 我想我找到了一种更简单的方法来实现与您的指南完全相同的效果,而且不需要任何技术知识,也不需要任何服务器的管理员权限。请告诉我您的看法。

概述

我们将配置一个邮件列表,然后使用电子邮件存档按顺序发送过去的对话。这些邮件将被转发,但不是像电子邮件客户端中的“转发”按钮那样(那样会覆盖邮件头并破坏缩进)。我们要做的是重新发送邮件(就像它们最初是发送给 Discourse 的那样)。

要求和假设

  • 访问之前的电子邮件交流:有人将所有的邮件存储在自己的电子邮件客户端中,并可以自愿进行转发——我们称此人为“约翰·多伊”。

  • 时间:电子邮件转发将非常缓慢,以便 Discourse 能够处理(可能需要几天时间,具体取决于存档大小,期间需要一台计算机运行并上传邮件)。

  • Thunderbird 客户端:我们还假设约翰·多伊使用名为“Thunderbird”的电子邮件客户端。虽然可能可以用其他客户端完成,但我尚未研究过。

本指南使用两个电子邮件地址作为占位符。您需要将它们替换为您自己的实际地址。

:incoming_envelope: johndoe@example.com 约翰·多伊的电子邮件(此人将转发整个邮件列表存档)

:postbox: discourse+mailinglist-3@discoursemail.com 用于将邮件转发到邮件列表类别的 Discourse 电子邮件地址(请参见设置 1 了解如何获取)

操作说明

以下是操作说明的基本概述:

  1. 按照 Mirroring a read-only mailing list in Discourse 上的指南创建您邮件列表的镜像。

    注意:这只将从今往后的邮件列表内容镜像到 Discourse。您仍然会错过过去的对话。这就是本指南其余部分的目的。

  2. 更改 Discourse 转发邮件的方式(我其实不确定这是否必要
    forwarded_behavior

  3. 编辑类别设置,在 自定义传入电子邮件地址: 设置项中,在现有内容末尾添加 |johndoe@example.com

    这里的管道符号 | 的作用类似于 ,表示您也希望 johndoe@example.com 能够向该类别发送邮件。

  4. 约翰·多伊在 Thunderbird 中安装扩展 Mail Redirect

    这是因为这不是普通的邮件转发。它的效果是发送邮件,就好像这些邮件最初是直接发送给 Discourse 的电子邮件地址,而不是发送给约翰·多伊的一样。

  5. 约翰·多伊进入扩展设置,将以下值设置为 1(默认值为 5)
    mail_redirect

    这将确保回复按顺序到达:否则 Discourse 无法及时识别回复是链式相关的,从而为每个回复创建一个新主题——但这会使转发过程变得非常缓慢。

  6. 约翰·多伊选中邮件列表的所有过往邮件,右键点击并选择 重定向。随后会打开一个新窗口,他将 discourse+mailinglist-3@discoursemail.com 添加为 重新发送目标

此后,约翰·多伊的电子邮件客户端将缓慢地将邮件存档发送到 Discourse。只需过一段时间检查一下,看看 Discourse 类别中是否充满了怀旧的老对话。

清理

  • 从该类别的 自定义传入电子邮件地址: 设置中移除约翰·多伊的电子邮件(别忘了移除 | 符号)。

  • 卸载 Mail Redirect 扩展——您很可能不再需要它,或者至少将 SMTP 连接数改回 5。

5 个赞

我们正尝试将 Mailman 邮件列表迁移到已运行的 Discourse 实例中。其中包含多个私有列表,我们需要为对应的分类设置相应的权限。如果在导入之前创建这些分类,所有私有列表的帖子都会被添加到“未分类”中(即自动公开)。

因此,我们有两个备选问题:

  • 是否有方法在导入前为导入的邮件列表设置权限(如果仅对管理员可见,对我们来说就已经足够)?
  • 是否有方法将邮件列表添加到现有分类中(该分类已预设好权限)?
3 个赞

我的 Discourse 论坛是 Yahoo 小组的延续,而该 Yahoo 小组本身又是 AOL 邮件列表的延续。去年秋天,面对 Yahoo 的大规模清理,我成功下载了该 Yahoo 小组的 .mbox 归档文件,并按照这些说明导入了那些消息。现在,我已经获取了 AOL 邮件列表的部分归档文件,也想导入这些消息。

这很简单,对吧?只需在 import/data/foo 下创建目录,将消息放入其中,然后运行导入脚本即可。但我想知道的是:如果日后我能够获取到完整(或更完整)的归档文件,我是否可以直接将这些文件放入 import/data/foo 中,再次运行导入脚本,让系统把新消息添加到同一个分类下?

  • 系统会自动去重吗?还是我会在两个归档中都出现的消息中看到多个副本?
    • 如果其中一个、另一个或两个归档都缺少 message-id 头部,这会改变上述问题的答案吗?
  • 在同一分类下再次导入是否会覆盖现有消息?
  • 我的大多数用户都处于邮件列表模式。如果我不希望给他们发送数百(甚至数千)条通知,以免引起骚扰并产生高昂的 Mailgun 费用,我是否应该在导入期间全局禁用电子邮件功能?
3 个赞

很遗憾,这是不可能的。

可以,你可以通过一些技巧让导入脚本重用现有的类别。

./launcher enter app
rails c

# 使用 URL 中显示的类别 ID,例如
# 当类别路径类似于 /c/howto/devs/56 时,ID 为 56
category = Category.find(56)

# 使用存储 mbox 文件的目录名称。例如,
# 如果文件存储在 import/data/foo 中,则目录名称应为 "foo"。
category.custom_fields["import_id"] = "directory_name"
category.save!

这出乎意料。我从未见过这种情况,但我从未尝试过将导入到具有非默认权限的现有类别中。

如果你无法使其正常工作,我建议你在论坛上发布一个公告,将站点切换到只读模式,创建备份,在不同的服务器上恢复备份,运行导入,配置类别权限,创建另一个备份,然后将其恢复到你的生产站点。

3 个赞

可以的。建议您保留 import/data/index.db 文件,以备将来查看之前导入的数据、修改生成的消息 ID 等用途。

是的,只要 Message-ID 头保持不变,已导入的消息就不会被重复导入。如果 Message-ID 头仅在其中一个归档中缺失,那您就麻烦了。当缺少该头时,我们会使用消息的 MD5 哈希值。您需要确保这两条消息要么具有相同的 Message-ID 头,要么生成相同的 MD5 哈希值。

不会。

在导入期间,所有发出的电子邮件都会被禁用。

3 个赞

是的,你可以诱使导入脚本重用现有类别。

好的,我们最终基本上就是这么做的(我们改用了 Category.find_by_name(),不过我想这只是语义上的区别)。很高兴知道我们选择了“正确”的方式 ;-)。谢谢!

3 个赞