Push сообщения с использованием firebase cloud message

Key capabilities

Integrates the Firebase platform

The functions you write can respond to events generated by various
Firebase and Google Cloud features, from
Firebase Authentication triggers
to Cloud Storage Triggers.

Integrate across Firebase features using the
Admin SDK
together with Cloud Functions, and integrate with third-party
services by writing your own webhooks.
Cloud Functions minimizes boilerplate
code, making it easier to use Firebase and Google Cloud inside your
function.

Zero maintenance Deploy your JavaScript or TypeScript code to our servers with one
command from the command line.
After that, Firebase automatically scales up computing resources to match
the usage patterns of your users. You never worry about credentials,
server configuration, provisioning new servers, or decommissioning old
ones.
Keeps your logic private and secure In many cases, developers prefer to control application logic on the
server to avoid tampering on the client side. Also, sometimes
it’s not desirable to allow that code to be reverse engineered.
Cloud Functions is fully insulated from the client, so you
can be sure it is private and always does exactly what you want.

Совет 7

Firebase плохо фильтрует данные. Например, вы можете сделать запрос, основанный только на одном условии, но вы не можете сделать запрос типа “WHERE… AND… AND”. В этой ситуации необходимо создать одно дополнительное поле в объекте, которое будет содержать значения, и выборка будет проходить с использованием этого значения. Например:

«movie1»: {
  «genre»: «комедия»,
  «name»: «Лучше не бывает»,
  «lead»: «Джек Николсон»,
  «genre_lead»: «комедия_Джек Николсон»
},…

1
2
3
4
5
6

«movie1″{

  «genre»»комедия»,

  «name»»Лучше не бывает»,

  «lead»»Джек Николсон»,

  «genre_lead»»комедия_Джек Николсон»

},…

Используя эту структуру, мы можем найти комедию с Джеком Николсоном в главной роли.

Как связать приложения с Firebase

Аккаунты Firebase и AdMob

Далее: Как связать приложения с Firebase

You must first enable user metrics in your AdMob account before you can link your AdMob apps to Firebase.

Firebase – это платформа для разработки мобильных и веб-приложений. С помощью этой платформы вы можете быстро разрабатывать качественные приложения, привлекать новых пользователей и повышать доходы. Платформа предлагает несколько интегрированных возможностей, таких как сервер для мобильных приложений, средства аналитики, инструменты для продвижения приложений и монетизации, которые можно использовать по отдельности или в любых сочетаниях.

Подробнее …

Что такое Google Аналитика?

Google Аналитика – один из многих продуктов в составе платформы Firebase. Это бесплатное решение для аналитики мобильных и веб-приложений теперь доступно пользователям AdMob. Связав приложения AdMob с Firebase, вы обеспечите поступление данных из Google Аналитики. Это поможет увеличить вовлеченность пользователей, повысить доходы и улучшить отчеты.

Подробную информацию вы найдете в документации по Firebase. Мы также рекомендуем посмотреть короткий видеообзор Google Аналитики:

Каковы преимущества связи с Firebase?

Связав приложения AdMob с Firebase, вы сможете использовать данные из аккаунта Google Аналитики. Чтобы эти данные поступали в AdMob, необходимо связать проект Firebase с аккаунтом Google Аналитики или Google Аналитики для Firebase. Подробнее о том, как связать приложения с Firebase…

После установки связи между AdMob и Firebase данные из Google Аналитики для Firebase будут доступны в аккаунте AdMob независимо от настроек доступа к ним. Поток данных из Аналитики поможет улучшить приложения и увеличить ваши доходы.

Как использовать Firebase SDK для Google Аналитики в приложениях AdMob

Внедрив Firebase SDK для Google Аналитики в свои приложения AdMob, вы сможете использовать функции Firebase и Google Аналитики. Это поможет повысить ваши доходы и уровень вовлеченности пользователей.

Чтобы внедрить Firebase SDK для Google Аналитики в свои приложения, выполните перечисленные ниже действия.

  1. Включите пользовательские показатели.
  2. Свяжите свои приложения AdMob с Firebase.
  3. Добавьте в приложение Firebase SDK для Google Аналитики. Сразу после этого вы можете приступить к использованию Аналитики. 

Преимущества связи с Firebase и использования Firebase SDK

В таблице ниже показаны возможности, предоставляемые на этапах интеграции Firebase SDK.

Функции Включение пользовательских показателей Связь с Firebase Добавление Firebase SDK для Google Аналитики
Использование Firebase SDK Нет. Пользовательские показатели включаются в аккаунте AdMob. Нет. Связь с аккаунтом Firebase осуществляется в аккаунте AdMob. Да. Подробнее о внедрении Firebase SDK для Google Аналитики…
Автоматический сбор событий статистики и свойств пользователя из вашего приложения
Просмотр пользовательских показателей, связанных с рекламой в вашем приложении в аккаунте AdMob
Просмотр прочих ключевых показателей вашего приложения в Firebase Console
Отметка в качестве конверсии для рекламных кампаний
Создание особых аудиторий
Экспорт и анализ данных в BigQuery
Ведение журнала пользовательских событий для анализа и моделирования

(например, Remote Config и A/B-тестирование)
 

Подробнее…

  • Как связать приложения с Google Аналитикой для Firebase
  • Введение в Google Аналитику для Firebase (видео)
  • Справочный центр Firebase
  • Как интегрировать Firebase в приложение на базе Android или iOS (сайт Google Developers)

Реакция на групповые изменения

Одна из вещей, которую мы хотим показывать пользователям Settle Up – это изменения, сделанные в каждой группе. Например, когда кто-то удаляет расход, должна быть запись, показывающая, кто её удалил. Это идеальный случай использования Cloud Functions. Отрывок ниже показывает, как мы это сделали (упрощен для ясности):

Несколько комментариев:

  • Мы используем Typescript, который транскомпилируется в чистый JavaScript (сейчас ES6). Это значительно упростило разработку во время пре-бета фазы, когда API менялся довольно часто, потому что компилятор сразу сообщал нам, что неисправно. Я очень рекомендую даже с бета-версией использовать Typescript, потому что это повышает вашу продуктивность без затрат.
  • Мы используем async/await, что упрощает код, убирая раздражающие коллбеки и приводя его к виду любого синхронного кода. В данный момент это функция есть только в Typescript, которая компилируется в генераторы. Хорошо, что она использует Promises, с которым Cloud Functions работает по умолчанию.
  • Мы используем шаблон admin.database.ServerValue.TIMESTAMP для установки времени сервера.
  • Мы должны узнать, кто внес изменение. Для этого пока нет задокументированного способа, только незадокументированный – пользовательский ID устанавливается внутрь event.auth.variable.uid. Существуют ситуации, когда так сделать нельзя (например, при внесени изменений прямо в консоли Firebase), поэтому вам нужна проверка для этого, иначе переменная by становится undefined и не может быть сохранена в базе данных Firebase.

2. Создайте приложение TutsplusAlerts

Создание проекта Android Studio

Сначала запустите Android Studio и создайте новый проект «TutsplusAlerts» с пустым действием .

Чтобы следовать дальше, убедитесь, что вы включили Firebase в свое приложение.

Добавить зависимость базы данных в реальном времени

Добавьте следующую зависимость в файл build.gradle:

Убедитесь, что вы синхронизировали свой проект после его добавления.

Запись в базу данных реального времени

Теперь мы собираемся сделать запись в базу по адресу .

Вашему приложению потребуется доступ на запись в базу данных. Только для демонстрационных целей вы можете установить правила безопасности для всех чтений и записей. В реальном приложении вы никогда не должны использовать такие небезопасные настройки.

Вы можете узнать больше о правилах безопасности Firebase в моей статье здесь, в Envato Tuts +.

Мобильная разработка Правила безопасности Firebase Chike Mgbemena

Запуск приложения

На этом этапе мы можем протестировать приложение и проверить, была ли наша Cloud Function успешно выполнена. Введите название и автора, а затем нажмите кнопку отправки. После этого зайдите на панель инструментов «Функции» и просмотрите логи. Должен появится наш новый лог.

Из логов, приведенных выше, мы видим, что мы успешно выполнили нашу облачную функцию и отправили сообщение с полезной payload на устройства, подписанные на тему «android». В следующем разделе мы будем использовать Firebase Cloud Messaging, чтобы устройства могли подписаться на тему, а затем обработать входящее сообщение с сервера, чтобы показать уведомление.

Совет 9

Если у вашего объекта в Firebase есть вложенный объект, который может быть пустым в некоторых случаях, лучше удалить этот объект полностью вместе с ключом вместо того, чтобы сохранять его ключ с пустым значением. Причиной этого является то, что парсер данных на стороне устройства может работать с определенным типом, и если этот тип будет отличаться от существующего, произойдет ошибка. Если объекта не будет, результат анализа вернется пустым. Например:

//Водитель внес данные об автомобиле

«driver»: {
«car»: {
«model»: «Audi TT»,
«vin»: «KL5TJ66E19B411947»,
},
«first_name»: «John»,
«last_name»: «Doe»
},…

//Водитель не внес данные об автомобиле
//Плохой пример

«driver»: {
«car»: «»,
«first_name»: «John»,
«last_name»: «Doe»
},…

//Хороший пример

«driver»: {
«first_name»: «John»,
«last_name»: «Doe»
},…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

//Водитель внес данные об автомобиле
 

«driver»{

«car»{

«model»»Audi TT»,

«vin»»KL5TJ66E19B411947»,

},

«first_name»»John»,

«last_name»»Doe»

},…

 
//Водитель не внес данные об автомобиле
//Плохой пример
 

«driver»{

«car»»»,

«first_name»»John»,

«last_name»»Doe»

},…

 
//Хороший пример
 

«driver»{

«first_name»»John»,

«last_name»»Doe»

},…

Как вы видите, в неправильном примере у парсера будет объект driver с вложенным объектом car, но на самом деле тип будет String.

Я бы хотел подчеркнуть, что в общем опыт моего использования Firebase положителен. Но как и всё в нашем мире, он имеет свои плюсы и минусы.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

getaddrinfo

EAI_ADDRFAMILY
У указанного сетевого узла нет сетевых адресов в запрашиваемом семействе
адресов.
EAI_AGAIN
Сервер имен вернул временную ошибку. Попробуйте позднее.
EAI_BADFLAGS
В hints.ai_flags содержатся неправильные флаги, либо hints.ai_flags
содержит AI_CANONNAME, а name — NULL.
EAI_FAIL
Сервер имен вернул постоянную ошибку.
EAI_FAMILY
Запрашиваемое семейство адресов не поддерживается.
EAI_MEMORY
Не хватает памяти.
EAI_NODATA
Указанный сетевой узел существует, однако не имеет ни одного определенного
сетевого адреса.
EAI_NONAME
node или service неизвестно; либо и node, и service равны NULL;
либо в hints.ai_flags указан флаг AI_NUMERICSERV, а service не
является числовой строкой порта.
EAI_SERVICE
Запрошенная служба не доступна для запрошенного типа сокета. Она может быть
доступна через другой тип сокета. Например, эта ошибка может возникнуть,
если в service указан «shell» (служба, доступная только для потоковых
сокетов) при указанном в hints.ai_protocol IPPROTO_UDP, либо указанном
в hints.ai_socktype SOCK_DGRAM. Также ошибка может возникнуть, если
service не равно NULL, а в hints.ai_socktype указано значение
SOCK_RAW (тип сокета, для которого концепция служб неприменима).
EAI_SOCKTYPE
Запрашиваемый тип сокетов не поддерживается. Такая ошибка может возникнуть,
если hints.ai_socktype и hints.ai_protocol противоречат друг другу
(например, SOCK_DGRAM и IPPROTO_TCP соответственно).
EAI_SYSTEM
Для других системных ошибок следует проверять errno.

Новые функции Firebase

Коротко о главном, а именно — про обновленные возможности Firebase (функции, которые на данный момент доступны в Beta):

Firebase Predictions (на вкладке Grow) — это функция, которая использует возможности искусственного интеллекта и данные из Firebase Analytics для того, чтобы предугадать, сделает ли пользователь определенное действие в будущем.

Хорошо звучит. А на деле? Смотрите на примере кейса от компании Halfbrick, которая занимается разработкой компьютерных игр.

Halfbrick применила функцию Predictions и получила на 20% больше пользователей.

Компания создала эксперимент, который состоял из трех групп, Halfbrick предлагала всплывающее окно с подарком внутриигровой валюты:

  • группа 0 — контрольная группа, которой не показывали всплывающее окно с подарком;
  • группа 1 — этой группе показывали всплывающее окно только после прохождения третьего уровня игры;
  • группа 2 — пользователи получали подарок в тот момент, когда функция Predictions определяла их как пользователей, которые планируют выйти из игры.

Так выглядело всплывающее окно для пользователей, которые планируют покинуть игру:

Результат теста:

Показывая в игре всплывающее окно пользователям, которые хотят уйти, Halfbrick смогли увеличить удержание 7-Day Active Users пользователей на 5% что приравнивается к 20% росту.

Groups 

1-Day Active Users

7-Day Active Users

Groups 0:

59.52%

25.35%

Groups 1:

59.07%

25.34%

Groups 2:

62.12%

30.24%

Firebase A/B Testing помогает улучшить приложение, упрощая запуск, анализ и масштабирование экспериментов по продукту и маркетингу.

Так ASO (App Store Optimization) невозможно себе представить без A/B тестирования. При этом Firebase дает возможность протестировать изменения в пользовательском интерфейсе и функциях вашего приложения при помощи встроенного инструментария.

Firebase A/B тестирование показывает, какие изменения в приложении влияют на наиболее важные бизнес-показатели. Вот, например, некоторые параметры, которые можно протестировать:

  • размещение кнопки;
  • форма кнопки;
  • цвет кнопки;
  • текст кнопки.

Пример результата Firebase A / B Testing:

Фрагмент интерфейса с отчетом Firebase A/B Testing:

ML Kit for Firebase. Простыми словами, ML Kit — это Machine Learning технология, которая позволяет:

  • распознавать текст;
  • обнаруживать и распознавать лица на фото или видео;
  • сканировать штрих-коды;
  • идентифицировать объекты на изображении.

Crashlytics. Вероятность сбоя приложения есть даже после его ручного и автоматического тестирования. Это может быть связано с рядом факторов: несовместимость с различными уровнями API, размерами экрана, памятью телефона, доступностью аппаратных датчиков и настройкой поставщиков в соответствии с их индивидуальными устройствами. Crashlytics поможет в диагностике этих проблем.

Почему это важно? Сбои делают пользователей несчастными. Пользователи могут удалить приложение, поставить ему плохую оценку, оставить отрицательный отзыв в магазине приложений или молча страдать, а потом установить приложение конкурента

И приложению, в котором много сбоев, не поможет даже самый лучший маркетинг.

При этом невозможно сделать так, чтобы в приложении совсем не было сбоев, но важно их минимизировать. Использование надежного и мощного инструмента для создания отчетов о сбоях важно для сбора информации и дает представление о том, как можно исправить эту проблему, чтобы пользователи остались довольны.. Cloud Functions

Небольшие фрагменты JavaScript-функций, развернутых на серверах Firebase. Они выполняются для разных событий, таких, например, как изменения базы данных Firebase или новый логин пользователя

Cloud Functions. Небольшие фрагменты JavaScript-функций, развернутых на серверах Firebase. Они выполняются для разных событий, таких, например, как изменения базы данных Firebase или новый логин пользователя.

Зачем это нужно? Для упрощения разработки приложений: то, что нужно было писать несколько раз для каждой платформы теперь можно написать один раз; приложения становятся безопасней — не нужно хранить ключи API разных сервисов в приложениях, а только на сервере.

Совет 6

При использовании аутентификации необходимо проверить, существует ли пользователь и верны ли введенные параметры аутентификации. Вы можете использовать исключения:

public void onComplete(@NonNull Task task) {

  if ( !task.isSuccessful() ) {

  try {
  throw task.getException();
  } catch(FirebaseAuthInvalidUserException e) {
  // No such user
  } catch(FirebaseAuthInvalidCredentialsException e) {
  //Credentials are invalid
  } catch(Exception e) {
  e.printStackTrace();
  }
  return;
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

publicvoidonComplete(@NonNull Task task){

  if(!task.isSuccessful()){

 try{

 throwtask.getException();

 }catch(FirebaseAuthInvalidUserExceptione){

 // No such user

 }catch(FirebaseAuthInvalidCredentialsExceptione){

 //Credentials are invalid

 }catch(Exceptione){

 e.printStackTrace();

 }

 return;

  }

}

Device group management

The following table lists the keys for creating device groups
and adding and removing members. For more information, see the
guide for your platform,

iOS+ or
Android.

Table 10. Device group management keys.

Parameter Usage Description
Required, string The operation to run.Valid values are ,
, and .
Required, string The user-defined name of the device group to create or modify.
Required (except for operation, string Unique identifier of the device group. This value
is returned in the response for a successful
operation, and is
required for all subsequent operations on the device group.
Required, array of strings The device tokens to add or remove. If you remove all existing
registration tokens from a device group, FCM deletes the device group.

Что такое «статический» сайт?

Вопреки тому, как это звучит, «статический» веб-сайт не означает, что ваш сайт должен выглядеть как страница GeoCities 1999 года. Вы по-прежнему можете свободно обслуживать контент JavaScript, даже полноценные одностраничные веб-приложения, созданные с такими фреймворками, как React.

статический просто означает, что ваш контент не меняется перед обслуживанием. Например, WordPress отвечает на запросы и изменяет содержимое страницы с помощью PHP, в зависимости от страницы, которую вы запросили. С другой стороны, статический веб-сайт — это просто обычный HTML, плюс любые изображения, CSS или JavaScript, которые вы отправляете вместе с ним. Вы можете изменить его на стороне клиента с помощью JavaScript после того, как он будет отправлен пользователю, как работает React, но даже в этом случае сам файл JavaScript является статическим.

Преимущество того, что ваш веб-сайт полностью статичен, заключается в том, что вам не нужен такой модный веб-сервер, как NGINX или Apache, для обслуживания вашего контента. Поскольку это просто статические файлы, многие провайдеры, такие как AWS и Google Cloud Platform, предлагают способы размещения таких веб-сайтов из облачных хранилищ.

Google предлагает такую ​​услугу для хостинга из хранилища Cloud Storage с использованием балансировщика нагрузки или CDN перед ним. Однако он предназначен для высокопроизводительных корпоративных сайтов и не является полностью бесплатным для использования. Для простых развертываний вы можете использовать платформу Google Firebase, которая предназначена для предоставления бэкэндов мобильным приложениям, но также включает в себя фантастическую службу размещения статического контента, которую вы можете использовать.

Преимущества связи с Firebase и использования Firebase SDK

В таблице ниже показаны возможности, предоставляемые на этапах интеграции Firebase SDK.

Функции Включение пользовательских показателей Связь с Firebase Добавление Firebase SDK для Google Аналитики
Использование Firebase SDK Нет. Пользовательские показатели включаются в аккаунте AdMob. Нет. Связь с аккаунтом Firebase осуществляется в аккаунте AdMob. Да. Подробнее о внедрении Firebase SDK для Google Аналитики…
Автоматический сбор событий статистики и свойств пользователя из вашего приложения
Просмотр пользовательских показателей, связанных с рекламой в вашем приложении в аккаунте AdMob
Просмотр прочих ключевых показателей вашего приложения в Firebase Console
Отметка в качестве конверсии для рекламных кампаний
Создание особых аудиторий
Экспорт и анализ данных в BigQuery
Ведение журнала пользовательских событий для анализа и моделирования

(например, Remote Config и A/B-тестирование)
 

API через Cloud Functions

На старте проекта хотелось сконцентрироваться на написании приложения, вместо того чтобы разворачивать инфраструктуру для API. Решили делать API на Cloud Functions. Продукт предлагает заманчивые возможности:

  • автоматическое масштабирование
  • инструменты мониторинга и логирования
  • тесная интеграция с другими продуктами Google Cloud

Триггерами для функции могут быть HTTP-запрос, сообщение в очереди Pub/Sub, изменение данных в Firestore, Auth или Storage. Сейчас мы используем почти все возможные. Когда админка загружает картинку, в Storage вызывается функция, чтобы сжать и сохранить эту картинку в нескольких размерах. Google Play Market шлет уведомления о покупках в Pub/Sub. На эти сообщения реагирует функция.

Firestore не умеет выполнять сложные запросы. К примеру, такой запрос уже не отработает:

Чтобы решить проблему, вызывается cloud-функция по изменению коллекции в Firestore. Изменение документа пользователя дублируется в MongoDB. Из MongoDB делать сложные выборки уже не проблема.

Рефакторинг структуры документов в Firestore вызывает проблемы. Проходить скриптом по коллекции с миллионами документов и пересохранять их в новой структуре может быть долго и дорого. Приходится учить программы работать с несколькими возможными структурами документов. Это зло:

if (user.platform === constants.iOS || user.appData.platform === constants.iOS) {

    // ...

}

Итак, зачем использовать облачные функции?

Итак, теперь вы видели ряд возможностей, которые могут предложить функции Cloud. Но зачем их использовать?

Запуск и настройка бэкенд части и серверов может быть настоящей головной болью — вам приходится обрабатывать такие проблемы, как масштабируемость и написание кода на серверных языках, но с облачными функциями эта сложность уменьшается. Кроме того, вычислительные интенсивные задачи могут выполняться в облаке, а не на клиентском устройстве (например, изменение размера изображения для загрузки или записи по нескольким путям вашей базы данных). Ваш код также будет более безопасным в облаке, чем на клиентском устройстве, чтобы вы могли безопасно хранить такие данные, как секретные ключи.

В этом уроке вы узнаете, как использовать триггеры базы данных реального времени, которые будут срабатывать при возникновении события записи базы данных. Затем мы увидим, как использовать службу Firebase Cloud Messaging для отправки уведомлений устройствам, подписанным на определенный топик. Мы создадим простое приложение под названием Tutsplus Alerts, которое будет отправлять уведомления подписчикам темы «android» всякий раз, когда доступна новая статья.

Зачем и как связать Firebase Analytics с Google Ads?

Связка Firebase с Google Ads нужна в том случае, если вы размещаете рекламу своего приложения в Google Ads. Связка Firebase и Google Ads позволит:

  1. Оценить эффективность рекламных кампаний с точки зрения количества и стоимости установок и ключевых in-App событий.
  2. Создать пользовательские списки аудиторий в Firebase и использовать их для мобильного ремаркетинга в Ads.

Чтобы связать Ads с Firebase в новом интерфейсе Ads, сперва переходим на вкладку «Настройки» — «Связанные аккаунты»:

В открывшемся окне выбираем Firebase:

Дальше в меню выбираем нужный проект и жмем на кнопку «Связать»:

Предоставление доступа к данным Аналитики компании Google

Продукты и сервисы Google

Если вы предоставите Google доступ к данным, мы сможем анализировать пользовательские действия и тенденции, чтобы повышать качество своих продуктов и сервисов. Эти данные могут использоваться для усовершенствования Google Реклама или AdMob – важнейших инструментов для проведения рекламных кампаний, которыми пользуются разработчики. Кроме того, вы можете заранее разрешить интеграцию новых функций, которые появятся в будущем. В этом случае, если для их работы требуется статистика, система начнет обрабатывать ваши данные Google Analytics для Firebase заранее, и вы сможете пользоваться новыми функциями сразу после их запуска.

Сравнение

На основе агрегированных анонимных данных мы разрабатываем инструменты и составляем публикации, которые позволяют вам лучше оценивать глобальные тенденции в своей отрасли

Обратите внимание, что система объединяет и делает анонимными данные с множества сайтов. Проследить, к каким аккаунтам, компаниям или пользователям относятся сведения, невозможно

Вы сможете сравнивать свою позицию на рынке с положением других компаний в отрасли и выявлять новые важные тенденции, такие как постоянное увеличение мобильного трафика год за годом.

Техническая поддержка

Специалистам службы поддержки Google иногда требуется доступ к вашему аккаунту для работы сервиса или решения технических проблем. Если вы сообщите о проблемах с аккаунтом, служба поддержки Google может помочь вам устранить неполадки.

Специалисты по аккаунтам

Специалисты Google по продажам и маркетингу делают все возможное, чтобы вам было удобнее работать с сервисами компании. Они смогут оценить эффективность различных стратегий и конфигураций аккаунтов.

Примечание. Если доступ к данным предоставляется другим продуктам и сервисам, применяются Условия использования последних.

Подробнее о предоставлении доступа к данным Аналитики компании Google…

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: