React Native Feedback Form Using AWS + Telegram

image

Before I started integrating the feedback form in one of my last projects, I tried to find a non-standard approach for solving the problem. Email needs some kind of SMTP server, and it’s just boring, especially for start-ups with a low message flow.

Messengers, this is a great alternative, and this is the path I chose. Telegram provides one of the easiest ways to create bots (if not the easiest). In its place can be anything - other messengers, phone calls (Amazon Connect) or Yandex Dialogs.

We are not trying to create a chatbot or interface for correspondence, we want this to look like a normal feedback form when the sender does not expect an immediate response. In the example below, the recipient will be able to reply to the user via e-mail, which he will leave in the form field.

Step 1: bot in Telegram, it can't be easier


We send a message @BotFatherwith the team /newbot, set the name and that's it! We got a token . We keep it to ourselves.

In addition, we need chat_id- the identification of the chat to which messages will be sent. Learn your chat_idlittle harder than getting a token , fortunately, there is a boat under the name @my_id_botthat will reveal your chat_id.

As a result, we have a token that we will use to "talk" with our bot, and chat_idwill be used to send a message to you personally. Now you can send a command to our bot /startand we can move on.

Telegram is shown as an example and because of the ease of use of bots, there may be another messenger or some other trigger in its place.

Step 2: Lambda as an intermediary


One of the options for setting up Telegram is to embed the entire request code and “talk” with the Telegram API into the React Native application code itself - a good option for the test. In my case, an API with several active Lambda functions was already configured, so my option includes Lambda as an intermediary layer between the application and the Telegram API. The code and instructions below can be used in the React Native project itself.

I used the Serverless framework to create a new function with the parameters below (format yaml).

sendTelegramMessage:
  description: used for contact form (${self:custom.stage})
  handler: lib/additional/sendMessageTelegram.handler
  environment: ${file(env_contact.yml):${self:custom.stage}}
  events:
    - http:
      path: contact
      method: POST
      private: false
  package:
    include:
      - lib/additional/sendMessageTelegram.js

Remember that the token and chat_idmust be strictly hidden. In this case, I am using the environment variable file that will be used in Lambda. As you can see from the lines above, Serverless will configure the Gateway API for POST requests. Also note that in this case, the API is open to everyone without any restrictions.

And here is the part of the Lambda code that will accept our request. Our front-end only collects information - email and message, and sends the request to our function, which will process the request in the Telegram API.

exports.handler = async (event) => {
  if (!event.body) {
    return createResponse(400, " ");
  }

  const data = JSON.parse(event.body);

  if (!data.email || !data.message) {
    return createResponse(400, " 'email'  'message' ");
  }

  try {
    const telegram = getTelegramConfig();

    await fetch(`${telegram.url}/bot${telegram.token}/sendMessage?${telegram.params(data.email, data.message)}`)

    return createResponse(200, " ");
  } catch (e) {
    return createResponse(500, e.message);
  }
};

Two helper functions getTelegramConfigfor creating a request URL and createResponsefor generating a standard response with a status code and a message.

const getTelegramConfig = () => {
  const token = process.env.TELEGRAM_BOT_TOKEN;
  const chatId = process.env.TELEGRAM_MAIN_CONTACT;

  if (!token || !chatId) {
    throw new Error("  ");
  }

  return {
    url: "https://api.telegram.org",
    token,
    params: (email, message) => qp.encode({
      chat_id: chatId,
      parse_mode: "Markdown",
      text: "  " + email + ": ```" + message + "```"
    })
  };
};

const createResponse = (statusCode, message) => ({
  statusCode,
  body: JSON.stringify({ message }, null, 2)
});

qp.encodeused to convert an object to a string like ?param1=value¶m2=value.

Now, if we make a POST request with the content below, we will receive our message on Telegram.

{
    "email": "privet@gmail.com",
    "message": "   .  . , ,  ,   . ,        .         ,   .  ,  .  .   ,     .          .  .    ,   "
}

image

Step 3: “Contact Us” in the React Native App


Remained part of the front end. Just as much as possible, requiring at least 10 characters for the message and checking the email address.

Part of JSX . Button- this is its component with additional parameters of inactive ( disabled) status and download status ( loading). Style is also at your discretion.

<View style={styles.container}>
  <TextInput
    value={sendersEmail}
    keyboardType="email-address"
    autoCapitalize="none"
    autoCompleteType="email"
    style={styles.input}
    onChangeText={onEmailChange}
    selectionColor="#555"
    placeholder=" "
    placeholderTextColor="#CCC"
    clearButtonMode="always"
    returnKeyType="done"
    disableFullscreenUI
  />
  <TextInput
    value={message}
    style={{ ...styles.input, ...styles.messageInput }}
    onChangeText={onMessageChange}
    selectionColor="#555"
    placeholder=" ..."
    placeholderTextColor="#CCC"
    clearButtonMode="always"
    returnKeyType="done"
    multiline
    textAlignVertical="top"
    disableFullscreenUI
  />
  <Button
    title=""
    loading={loading}
    disabled={disabled}
    onPress={onSendPress}
  />
</View>

JavaScript part.

const [ sendersEmail, setSendersEmail ] = useState("");
  const [ message, setMessage ] = useState("");

  const [ loading, setLoading ] = useState(false);

  const onEmailChange = (value) => {
    setSendersEmail(value);
  };

  const onMessageChange = (value) => {
    setMessage(value);
  };

  const onSendPress = () => {
    setLoading(true);

    const url = "https://api.example.com/contact";

    fetch(url, {
      method: "POST",
      body: JSON.stringify({
        email: sendersEmail,
        message
      })
    })
    .then(() => {
      setLoading(false);
    })
    .catch(() => {
      setLoading(false);

      Alert.alert("", "    .");
    });
  };

  const isValidEmail = () => {
    const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

    return emailRegex.test(sendersEmail.toLowerCase());
  };


  const disabled = message.length < 10 || !isValidEmail();

The Submit button will be inactive until the user enters the correct email address and at least a 10-character message. Remember that in this example, the API is open.

image

Like this.

What is missing


  • I did not touch part of the API, this is a separate topic, many other articles can be found about this. I also recommend that you familiarize yourself with the Serverless framework.
  • In this example, we receive a message in Telegram, but we will have to answer by email. There are many different options and this is just an example.

useful links


In English
https://core.telegram.org/bots/api#sendmessage
https://core.telegram.org/bots/api#markdown-style
https://facebook.imtqy.com/react-native/docs/ textinput

All Articles