<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nassim Dehouche</title>
    <description>The latest articles on DEV Community by Nassim Dehouche (@ndehouche).</description>
    <link>https://dev.to/ndehouche</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F794205%2F1c86ca42-6bd8-41b8-9506-a3964e863ae3.png</url>
      <title>DEV Community: Nassim Dehouche</title>
      <link>https://dev.to/ndehouche</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ndehouche"/>
    <language>en</language>
    <item>
      <title>How to Solve "Struct Containing a (Nested) Mapping Cannot be Constructed" in Solidity</title>
      <dc:creator>Nassim Dehouche</dc:creator>
      <pubDate>Sun, 13 Mar 2022 05:08:04 +0000</pubDate>
      <link>https://dev.to/ndehouche/how-to-solve-struct-containing-a-nested-mapping-cannot-be-constructed-in-solidity-4kab</link>
      <guid>https://dev.to/ndehouche/how-to-solve-struct-containing-a-nested-mapping-cannot-be-constructed-in-solidity-4kab</guid>
      <description>&lt;p&gt;This compilation error is encountered when you try to instantiate a struct that contains a mapping as an attribute. I needed this data structure to write &lt;a href="https://github.com/ndehouche/DecentralizedClinicalTrials/blob/main/README.md"&gt;this contract&lt;/a&gt; to allow patients to license the datasets resulting from their clinical trials, as part of an &lt;a href="https://docs.algovera.ai/blog/2022/01/27/grant%20recipients%20for%20algovera%20grants%20r1/"&gt;Algovera&lt;/a&gt; grant.&lt;/p&gt;

&lt;p&gt;This type of one-to-many data structure can be useful, for instance, to encode a marketplace for time-limited licenses to some content. In addition to the content to be licensed, the struct would need to map licensees' addresses with a timestamp for the start of their license, so that their access can be controlled.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;struct content{ &lt;br&gt;
  address payable owner;&lt;br&gt;
  bytes32 hash;&lt;br&gt;
  mapping(address =&amp;gt; uint) licenses;&lt;br&gt;
  }&lt;br&gt;
mapping(address =&amp;gt; content[]) public contents;&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Visually it would look like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xNOUqw_N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbjv7si3moecffjb5hnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xNOUqw_N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbjv7si3moecffjb5hnq.png" alt="Image description" width="561" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "struct containing a (nested) mapping cannot be constructed" error would appear when trying to instantiate the struct, the usual way, with a recent compiler version. Like this, for instance:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function submitContent(bytes32 _hash) public payable&lt;br&gt;
returns(uint _id)&lt;br&gt;
{&lt;br&gt;
_id=contents[msg.sender].length;&lt;br&gt;
contents[msg.sender].push(content({&lt;br&gt;
owner: payable(msg.sender),&lt;br&gt;
hash:_hash&lt;br&gt;
return(_id);&lt;br&gt;
}));&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
Indeed, since version 0.7.0, structs or arrays that contain a mapping can only be used in storage, so Solidity complains because variables in an instantiation function would be in memory by default. &lt;br&gt;
You can read about this change in the &lt;a href="https://docs.soliditylang.org/en/v0.7.1/070-breaking-changes.html#mappings-outside-storage"&gt;Solidity documentaiton&lt;/a&gt;. The relevant part is under "Removal of Unused or Unsafe Features":&lt;br&gt;
 &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ln8kzE6r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzd9a09mgn90znkmjzjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ln8kzE6r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzd9a09mgn90znkmjzjl.png" alt="Image description" width="880" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to write this short note because some top results when Googling this error are either outdated, or to the extent of "just don't use a mapping in a struct bro". A struct containing a mapping cannot be equivalently modeled with a separate mapping; reading and writing would require much more computation.&lt;/p&gt;

&lt;p&gt;The workaround is quite simple, we just have to declare the struct in storage before we instantiate a pointer to it. Our &lt;em&gt;submitContent()&lt;/em&gt; function would have to look like this:&lt;br&gt;
&lt;code&gt;function submitContent(bytes32 _hash) public payable&lt;br&gt;
returns(uint _id)&lt;br&gt;
{  &lt;br&gt;
_id= contents[msg.sender].length;&lt;br&gt;
content[] storage c = contents[msg.sender];&lt;br&gt;
c.push();&lt;br&gt;
c[_id].owner = payable(msg.sender);&lt;br&gt;
d[_id].hash=_hash;&lt;br&gt;
return _id;&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

</description>
      <category>soldity</category>
      <category>ethereum</category>
      <category>mapping</category>
      <category>struct</category>
    </item>
    <item>
      <title>A Discord Bot to Save Messages as Notion Notes</title>
      <dc:creator>Nassim Dehouche</dc:creator>
      <pubDate>Tue, 25 Jan 2022 11:13:09 +0000</pubDate>
      <link>https://dev.to/ndehouche/creating-a-discord-bot-to-save-messages-as-notion-notes-with-nodejs-and-discordjs-f8</link>
      <guid>https://dev.to/ndehouche/creating-a-discord-bot-to-save-messages-as-notion-notes-with-nodejs-and-discordjs-f8</guid>
      <description>&lt;p&gt;Twitter: &lt;a href="https://twitter.com/ndehouche"&gt;https://twitter.com/ndehouche&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Project overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Some of your best ideas and content are generated on-the-fly in Discord conversations. In this tutorial, we are going to create a Discord bot that will allow you to automatically send messages from a Discord server to Notion, if an Admin reacts to them with a specified emoji, say ✍️. &lt;/p&gt;

&lt;p&gt;This can notably be useful to automatically save FAQs, user suggestions, or just generally great content from your Discord server. &lt;/p&gt;

&lt;p&gt;First, we are going to create a Discord bot, save its access token, and invite it in our Discord server. You can skip the following section, if you are already familiar with creating Discord bots.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to create a Discord Bot&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log on to the Discord.&lt;/li&gt;
&lt;li&gt;Go to the &lt;a href="https://discord.com/developers/applications"&gt;applications&lt;/a&gt; page.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the “New Application” button, give a name to your application, and click “Create”. &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xrJPX5yF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xeauculu5p83tj9ijspp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xrJPX5yF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xeauculu5p83tj9ijspp.png" alt="Image description" width="262" height="77"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ywdHkQ5m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdrxez0gnu2gf7rj18o8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ywdHkQ5m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdrxez0gnu2gf7rj18o8.png" alt="Image description" width="552" height="468"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to the “Bot” tab and click “Add Bot”, and confirm with “Yes, do it!”.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UhHbi5AX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmjrmdi98xtoytu4cddd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UhHbi5AX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmjrmdi98xtoytu4cddd.png" alt="Image description" width="407" height="480"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IuuLi1NV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bpw7xzfmg47ol0sk2o1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IuuLi1NV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bpw7xzfmg47ol0sk2o1.png" alt="Image description" width="147" height="82"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M4N6Nr4t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/27mcrsqnrligzky85dnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M4N6Nr4t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/27mcrsqnrligzky85dnp.png" alt="Image description" width="533" height="233"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a&gt;5.&lt;/a&gt; Copy the access token you get and save it somewhere safe.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dvaBL4HL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7ggh2ms16i793qis0ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dvaBL4HL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7ggh2ms16i793qis0ym.png" alt="Image description" width="271" height="136"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We are now going to invite our bot in our server. Go "Back to Applications" and open your bot's page.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_jLnAOE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5w56s662wgy1hqkujok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_jLnAOE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5w56s662wgy1hqkujok.png" alt="Image description" width="232" height="54"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u4uJgYd6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ka07h57kla3bxqoqnuh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u4uJgYd6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ka07h57kla3bxqoqnuh6.png" alt="Image description" width="205" height="317"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the “OAuth2” tab. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nYzLiyya--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tabiglwcdz2h9od4ajy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nYzLiyya--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tabiglwcdz2h9od4ajy9.png" alt="Image description" width="405" height="647"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to URL Generator and select “bot” under “scopes”.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zi2NTooM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ay2ozn706nb6xu7mucr7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zi2NTooM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ay2ozn706nb6xu7mucr7.png" alt="Image description" width="880" height="286"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this application, our bot needs to read and write messages, as well as possibly manage emojis if you want to use custom reactions, and read message history, if you want to add support for messages posted before the addition of the bot to the server. We select the corresponding permissions, and copy the generated URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gc8tnc1Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz5v482n5fe021h6ngvg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gc8tnc1Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz5v482n5fe021h6ngvg.png" alt="Image description" width="880" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This URL will open a Discord permission page that will allow you add the bot to any server you manage. &lt;/p&gt;

&lt;p&gt;Now let's create a Notion integration that will allow our Discord bot to write into a Notion page. You can skip the following section if you are already familiar with the Notion API. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to create an integration with the Notion API&lt;/strong&gt;&lt;br&gt;
The Notion API is currently in Beta, so a few things described here may change, but but as of the time of this writing (January 2022), here is how to create a Notion integration that will allow us to write into a Notion page from an external application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://www.notion.com/my-integrations"&gt;My Integrations&lt;/a&gt; page and click the "+ New integration" button.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yGiF_9TS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jxcj70zxtudlhs14uzyu.png" alt="Image description" width="253" height="62"&gt;
&lt;/li&gt;
&lt;li&gt;Give your integration a name and select the workspace you would like it to be associated with.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7XiEgRY3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hldt08xho42h9nlkagui.png" alt="Image description" width="596" height="590"&gt;
We won't need any user information for this application, so select No User Information and submit your form. 
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lstXTobY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cnc5jooghoaseg3amap7.png" alt="Image description" width="647" height="396"&gt;
&lt;a&gt; 3.&lt;/a&gt; Copy the "Internal Integration Token" you get and save it somewhere safe.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vx3Ajklm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t9fpteiah0mgzq17cltn.png" alt="Image description" width="601" height="172"&gt;
&lt;/li&gt;
&lt;li&gt;Create a new page in your Notion workspace, and insert a new database in it, by typing /table and selecting a full page table. 
&lt;a&gt;5.&lt;/a&gt; Click on the Share button and use the selector to find your integration by its name, then click Invite and copy the link you get. 
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A8HMtx8V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9wmqn9e5th8i00mm8jh.gif" alt="Image description" width="880" height="276"&gt;
This link should be of the form &lt;a href="https://www.notion.so/"&gt;https://www.notion.so/&lt;/a&gt;?v=...
The 32 characters you get right before the question mark are you database ID. Save this ID somewhere safe.
We can now finally get Discord and Notion to talk to each other.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How to connect Discord and Notion in Node.js&lt;/strong&gt;&lt;br&gt;
We need to have &lt;a href="https://nodejs.org/en/download/"&gt;npm and Node.js&lt;/a&gt; installed and the &lt;a href="https://www.npmjs.com/package/dotenv"&gt;dotenv&lt;/a&gt; package to import the various API keys into Node.js.&lt;/p&gt;

&lt;p&gt;We will also need to install the &lt;a href="https://www.npmjs.com/package/@notionhq/client"&gt;Notion SDK&lt;/a&gt; and &lt;a href="https://discord.js.org/"&gt;discord.js&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a folder for your project and a &lt;code&gt;.env&lt;/code&gt; file in which you will safely store our environment variables for Discord and Notion. &lt;br&gt;
The following environment variables are respectively the Discord access token we got here, the Notion integration token we got here, and the Notion database ID we got here.&lt;br&gt;
&lt;code&gt;DISCORD_TOKEN='...'&lt;br&gt;
NOTION_KEY='secret_...'&lt;br&gt;
NOTION_DATABASE_ID='...'&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now we can start writing our Node.js program, let's call it &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;As early as possible in your program, import and configure dotenv.&lt;br&gt;
&lt;code&gt;require('dotenv').config()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can then import our secret environment variables.&lt;br&gt;
&lt;code&gt;const token = process.env.DISCORD_TOKEN&lt;br&gt;
const NOTION_KEY=process.env.NOTION_KEY&lt;br&gt;
const NOTION_DATABASE_ID=process.env.NOTION_DATABASE_ID&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We start a new notion client instance.&lt;br&gt;
&lt;code&gt;var {Client} =require("@notionhq/client");&lt;br&gt;
const notion = new Client({ auth: NOTION_KEY })&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We add a function that can write in a notion page, using &lt;a href="https://developers.notion.com/reference/page"&gt;notion.pages.create&lt;/a&gt;.&lt;br&gt;
&lt;code&gt;async function addItem(text) {&lt;br&gt;
try {&lt;br&gt;
const response = await notion.pages.create({&lt;br&gt;
  parent: { database_id: NOTION_DATABASE_ID },&lt;br&gt;
  properties: {&lt;br&gt;
    title: { &lt;br&gt;
      title:[&lt;br&gt;
        {&lt;br&gt;
          "text": {&lt;br&gt;
            "content": text}}]}},})&lt;br&gt;
console.log(response)&lt;br&gt;
console.log("Success! Entry added.")} catch (error) {&lt;br&gt;
console.error(error.body)&lt;br&gt;
}}&lt;/code&gt;&lt;br&gt;
And we are done with Notion! Let's now turn our attention to Discord.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We import the discord.js module, with &lt;em&gt;Client&lt;/em&gt;, &lt;em&gt;Intents&lt;/em&gt;, and &lt;em&gt;MessageEmbed&lt;/em&gt;.&lt;br&gt;
&lt;code&gt;var { Client, Intents, MessageEmbed } = require('discord.js')&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create an instance of a Discord client&lt;br&gt;
&lt;code&gt;const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS] });&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Emit a ready event. This is important. Your bot will only start reacting to messages after ready is emitted. &lt;br&gt;
&lt;code&gt;client.on('ready', () =&amp;gt; {&lt;br&gt;
console.log('I am ready!');});&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now we can write our algorithm to saves messages to Notion, when an Admin (you can use any other role or combination of roles) reacts to them with emoji ✍️. We also make the bot confirm that it has performed this action by quoting the original message and displaying the author's name and profile photo.&lt;br&gt;
&lt;code&gt;client.on('messageReactionAdd', (reaction, user) =&amp;gt; {&lt;br&gt;
if (user.bot) return;&lt;br&gt;
console.log('reaction');&lt;br&gt;
if(reaction.emoji.name === "✍️") {&lt;br&gt;
if (reaction.message.member.roles.cache.some(role =&amp;gt; role.name === 'Admin')) {&lt;br&gt;
let embed = new MessageEmbed()&lt;br&gt;
.setTitle('Content added to Notion')&lt;br&gt;
.setDescription(reaction.message.content)&lt;br&gt;
.setAuthor({name: reaction.message.author.tag, iconURL: reaction.message.author.displayAvatarURL()} )&lt;br&gt;
addItem(reaction.message.content)&lt;br&gt;
reaction.message.channel.send({embeds: [embed]}).catch(console.error)&lt;br&gt;
return;}}});&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, we log our bot in&lt;br&gt;
&lt;code&gt;client.login(token);&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can now run node &lt;code&gt;index.js&lt;/code&gt;. Here is what the bot should be able to do.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--88e-85eJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gp6o924cctdzrrui9b6q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--88e-85eJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gp6o924cctdzrrui9b6q.gif" alt="Image description" width="880" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete program for your copypasta pleasure. &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
require('dotenv').config()
// Discord token
const token = process.env.DISCORD_TOKEN

// Notion API keys
const NOTION_KEY=process.env.NOTION_KEY
const NOTION_DATABASE_ID=process.env.NOTION_DATABASE_ID

// Notion client
var {Client} =require("@notionhq/client");
const notion = new Client({ auth: NOTION_KEY })

// Function to write to Notion
async function addItem(text) {
  try {
    const response = await notion.pages.create({
      parent: { database_id: NOTION_DATABASE_ID },
      properties: {
        title: { 
          title:[
            {
              "text": {
                "content": text
              }}]}},})
    console.log(response)
    console.log("Success! Entry added.")
  } catch (error) {
    console.error(error.body) }}


// Import the discord.js module
var { Client, Intents, MessageEmbed } = require('discord.js');
// Create an instance of a Discord client
const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS] });

// Ready event
client.on('ready', () =&amp;gt; {
  console.log('I am ready!');

});


// Bot algorithm
client.on('messageReactionAdd', (reaction, user) =&amp;gt; {
   if (user.bot) return;
   console.log('reaction');
   if(reaction.emoji.name === "✍️") {
if (reaction.message.member.roles.cache.some(role =&amp;gt; role.name === 'Admin')) {
   let embed = new MessageEmbed()
   .setTitle('Content added to Notion')
   .setDescription(reaction.message.content)
   .setAuthor({name: reaction.message.author.tag, iconURL: reaction.message.author.displayAvatarURL()} )
   addItem(reaction.message.content)
   reaction.message.channel.send({embeds: [embed]}).catch(console.error)
   return;}}});

// Log our bot in
client.login(token);


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>discord</category>
      <category>notion</category>
      <category>bot</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
