Exemple #1
0
        private void OnGymDetailsAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <GymDetailsData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Gym Details Found [Alarm: {e.Alarm.Name}, GymId: {e.Data.GymId}, InBattle={e.Data.InBattle}, Team={e.Data.Team}]");

            var gymDetails = e.Data;
            var loc        = GeofenceService.GetGeofence(e.Alarm.GeofenceItems, new Location(gymDetails.Latitude, gymDetails.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"Failed to lookup city from coordinates {pokemon.Latitude},{pokemon.Longitude} {pkmn.Name} {pokemon.IV}, skipping...");
                return;
            }

            if (!_servers.ContainsKey(e.GuildId))
            {
                return;
            }

            if (!_whConfig.Instance.Servers.ContainsKey(e.GuildId))
            {
                return;
            }

            try
            {
                var oldGym  = _whm.Gyms[gymDetails.GymId];
                var changed = oldGym.Team != gymDetails.Team || gymDetails.InBattle || oldGym.SlotsAvailable != gymDetails.SlotsAvailable;
                if (!changed)
                {
                    return;
                }

                var client    = _servers[e.GuildId];
                var eb        = gymDetails.GenerateGymMessage(e.GuildId, client, _whConfig.Instance, e.Alarm, _whm.Gyms[gymDetails.GymId], loc?.Name ?? e.Alarm.Name);
                var name      = gymDetails.GymName;
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = eb.Username,
                    AvatarUrl = eb.IconUrl,
                    Content   = eb.Description,
                    Embeds    = eb.Embeds
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);
                Statistics.Instance.GymAlarmsSent++;

                // Gym team changed, set gym in gym cache
                _whm.SetGym(gymDetails.GymId, gymDetails);

                Statistics.Instance.GymAlarmsSent++;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
        public static DiscordWebhookMessage BuildWebhookMessage(string ip, List <RequiredServer> servers)
        {
            var message = new DiscordWebhookMessage();

            for (var i = 0; i < servers.Count; i++)
            {
                var server = servers[i];
                var embed  = new DiscordWebhookEmbed()
                {
                    title       = $"```{server.ServerName}```",
                    description = $"Connect: steam://connect/{ip}:{server.Port}",
                    color       = 4437377 // TODO : Get a red and check if server's running or not
                }
                .AddStatus(server)
                .AddAddress(ip, server.Port)
                .AddLocation(server)
                .AddGame(server)
                .AddCurrentMap(server)
                .AddPlayers(server)
                .AddFooter();
                message.embeds.Add(embed);
            }

            return(message);
        }
Exemple #3
0
        private void OnRaidAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <RaidData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Raid Found [Alarm: {e.Alarm.Name}, Raid: {e.Data.PokemonId}, Level: {e.Data.Level}, StartTime: {e.Data.StartTime}]");

            var raid = e.Data;
            var loc  = GeofenceService.GetGeofence(e.Alarm.GeofenceItems, new Location(raid.Latitude, raid.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[RAID] Failed to lookup city from coordinates {raid.Latitude},{raid.Longitude} {pkmn.Name} {raid.Level}, skipping...");
                return;
            }

            if (!_servers.ContainsKey(e.GuildId))
            {
                return;
            }

            if (!_whConfig.Instance.Servers.ContainsKey(e.GuildId))
            {
                return;
            }

            try
            {
                var server    = _whConfig.Instance.Servers[e.GuildId];
                var client    = _servers[e.GuildId];
                var eb        = raid.GenerateRaidMessage(e.GuildId, client, _whConfig.Instance, e.Alarm, loc.Name);
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = eb.Username,
                    AvatarUrl = eb.IconUrl,
                    Content   = eb.Description,
                    Embeds    = eb.Embeds
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);
                if (raid.IsEgg)
                {
                    Statistics.Instance.EggAlarmsSent++;
                }
                else
                {
                    Statistics.Instance.RaidAlarmsSent++;
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #4
0
        private void OnGymDetailsAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <GymDetailsData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Gym Details Found [Alarm: {e.Alarm.Name}, GymId: {e.Data.GymId}, InBattle={e.Data.InBattle}, Team={e.Data.Team}]");

            var gymDetails = e.Data;
            var loc        = _whm.GeofenceService.GetGeofence(e.Alarm.Geofences, new Location(gymDetails.Latitude, gymDetails.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"Failed to lookup city from coordinates {pokemon.Latitude},{pokemon.Longitude} {pkmn.Name} {pokemon.IV}, skipping...");
                return;
            }

            try
            {
                if (!_gyms.ContainsKey(gymDetails.GymId))
                {
                    _gyms.Add(gymDetails.GymId, gymDetails);
                }

                var oldGym  = _gyms[gymDetails.GymId];
                var changed = oldGym.Team != gymDetails.Team;// || /*oldGym.InBattle != gymDetails.InBattle ||*/ gymDetails.InBattle;
                if (!changed)
                {
                    return;
                }

                var eb        = gymDetails.GenerateGymMessage(_client, _whConfig, e.Alarm, oldGym, loc?.Name ?? e.Alarm.Name);
                var name      = gymDetails.GymName;
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = name,
                    AvatarUrl = gymDetails.Url,
                    Embeds    = new List <DiscordEmbed> {
                        eb
                    }
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);

                _gyms[gymDetails.GymId] = gymDetails;

                //Statistics.Instance.PokemonSent++;
                //Statistics.Instance.IncrementPokemonStats(pokemon.Id);
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #5
0
        private async void OnPokemonAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <PokemonData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Pokemon Found [Alarm: {e.Alarm.Name}, Pokemon: {e.Data.Id}, Despawn: {e.Data.DespawnTime}]");

            var pokemon = e.Data;
            var loc     = GeofenceService.GetGeofence(e.Alarm.Geofences, new Location(pokemon.Latitude, pokemon.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[POKEMON] Failed to lookup city from coordinates {pokemon.Latitude},{pokemon.Longitude} {pkmn.Name} {pokemon.IV}, skipping...");
                return;
            }

            if (!_servers.ContainsKey(e.GuildId))
            {
                return;
            }

            if (!_whConfig.Servers.ContainsKey(e.GuildId))
            {
                return;
            }

            try
            {
                var server = _whConfig.Servers[e.GuildId];
                var client = _servers[e.GuildId];
                var eb     = await pokemon.GeneratePokemonMessage(e.GuildId, client, _whConfig, e.Alarm, loc.Name);

                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = eb.Username,
                    AvatarUrl = eb.IconUrl,
                    Content   = eb.Description,
                    Embeds    = eb.Embeds
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);
                Statistics.Instance.PokemonAlarmsSent++;

                if (pokemon.IV == "100%")
                {
                    Statistics.Instance.Add100Percent(pokemon);
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #6
0
        private void OnPokestopAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <PokestopData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Pokestop Found [Alarm: {e.Alarm.Name}, PokestopId: {e.Data.PokestopId}, LureExpire={e.Data.LureExpire}, InvasionExpire={e.Data.IncidentExpire}]");

            var pokestop = e.Data;
            var loc      = _whm.GeofenceService.GetGeofence(e.Alarm.Geofences, new Location(pokestop.Latitude, pokestop.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[POKESTOP] Failed to lookup city for coordinates {pokestop.Latitude},{pokestop.Longitude}, skipping...");
                return;
            }

            string icon;

            if (pokestop.HasInvasion)
            {
                //TODO: Load from local file
                icon = "http://images2.fanpop.com/image/photos/11300000/Team-Rocket-Logo-team-rocket-11302897-198-187.jpg";
            }
            else if (pokestop.HasLure)
            {
                icon = string.Format(_whConfig.Urls.QuestImage, Convert.ToInt32(pokestop.LureType));//"https://serebii.net/pokemongo/items/luremodule.png";
            }
            else
            {
                icon = pokestop.Url;
            }

            try
            {
                var eb        = pokestop.GeneratePokestopMessage(_client, _whConfig, e.Alarm, loc?.Name ?? e.Alarm.Name);
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = pokestop.Name ?? "Unknown Pokestop",
                    AvatarUrl = icon,
                    Embeds    = new List <DiscordEmbed> {
                        eb
                    }
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);

                //Statistics.Instance.QuestsSent++;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #7
0
        private void OnPokestopAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <PokestopData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Pokestop Found [Alarm: {e.Alarm.Name}, PokestopId: {e.Data.PokestopId}, LureExpire={e.Data.LureExpire}, InvasionExpire={e.Data.IncidentExpire}]");

            var pokestop = e.Data;
            var loc      = GeofenceService.GetGeofence(e.Alarm.GeofenceItems, new Location(pokestop.Latitude, pokestop.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[POKESTOP] Failed to lookup city for coordinates {pokestop.Latitude},{pokestop.Longitude}, skipping...");
                return;
            }

            if (!_servers.ContainsKey(e.GuildId))
            {
                return;
            }

            if (!_whConfig.Instance.Servers.ContainsKey(e.GuildId))
            {
                return;
            }

            try
            {
                var client    = _servers[e.GuildId];
                var eb        = pokestop.GeneratePokestopMessage(e.GuildId, client, _whConfig.Instance, e.Alarm, loc?.Name ?? e.Alarm.Name);
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = eb.Username ?? Translator.Instance.Translate("UNKNOWN_POKESTOP"),
                    AvatarUrl = eb.IconUrl,
                    Content   = eb.Description,
                    Embeds    = eb.Embeds
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);
                if (pokestop.HasInvasion)
                {
                    Statistics.Instance.InvasionAlarmsSent++;
                }
                else if (pokestop.HasLure)
                {
                    Statistics.Instance.LureAlarmsSent++;
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #8
0
        private async void OnPokemonAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <PokemonData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Pokemon Found [Alarm: {e.Alarm.Name}, Pokemon: {e.Data.Id}, Despawn: {e.Data.DespawnTime}]");

            var pokemon = e.Data;
            var pkmn    = Database.Instance.Pokemon[pokemon.Id];
            var loc     = _whm.GeofenceService.GetGeofence(e.Alarm.Geofences, new Location(pokemon.Latitude, pokemon.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[POKEMON] Failed to lookup city from coordinates {pokemon.Latitude},{pokemon.Longitude} {pkmn.Name} {pokemon.IV}, skipping...");
                return;
            }

            try
            {
                var form = pokemon.Id.GetPokemonForm(pokemon.FormId.ToString());
                //var costume = e.Pokemon.Id.GetCostume(e.Pokemon.Costume.ToString());
                //var costumeFormatted = (string.IsNullOrEmpty(costume) ? "" : " " + costume);
                var pkmnImage = pokemon.Id.GetPokemonImage(_whConfig.Urls.PokemonImage, pokemon.Gender, pokemon.FormId, pokemon.Costume);
                var eb        = await pokemon.GeneratePokemonMessage(_client, _whConfig, pokemon, e.Alarm, loc.Name, pkmnImage);

                var name      = $"{pkmn.Name}{pokemon.Gender.GetPokemonGenderIconValue()}{form}";
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = name,
                    AvatarUrl = pkmnImage,
                    Embeds    = new List <DiscordEmbed> {
                        eb
                    }
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);

                Statistics.Instance.PokemonSent++;
                Statistics.Instance.IncrementPokemonStats(pokemon.Id);

                if (pokemon.IV == "100%")
                {
                    Statistics.Instance.Add100Percent(pokemon);
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #9
0
        private void OnWeatherAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <WeatherData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Weather Found [Alarm: {e.Alarm.Name}, S2CellId: {e.Data.Id}, Condition={e.Data.GameplayCondition}, Severity={e.Data.Severity}]");

            var weather = e.Data;
            var loc     = GeofenceService.GetGeofence(e.Alarm.GeofenceItems, new Location(weather.Latitude, weather.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"Failed to lookup city from coordinates {pokemon.Latitude},{pokemon.Longitude} {pkmn.Name} {pokemon.IV}, skipping...");
                return;
            }

            if (!_servers.ContainsKey(e.GuildId))
            {
                return;
            }

            if (!_whConfig.Instance.Servers.ContainsKey(e.GuildId))
            {
                return;
            }

            try
            {
                var client    = _servers[e.GuildId];
                var eb        = weather.GenerateWeatherMessage(e.GuildId, client, _whConfig.Instance, e.Alarm, loc?.Name ?? e.Alarm.Name);
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = eb.Username,
                    AvatarUrl = eb.IconUrl,
                    Content   = eb.Description,
                    Embeds    = eb.Embeds
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);

                // Weather changed, set weather in weather cache
                _whm.SetWeather(weather.Id, weather.GameplayCondition);

                Statistics.Instance.WeatherAlarmsSent++;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
        public void SendMessage(string[] args, EMessageType messageType)
        {
            DiscordWebhook webhook = configuration.Webhooks.FirstOrDefault(x => x.WebhookType == messageType.ToString());

            if (webhook == null || string.IsNullOrEmpty(webhook.WebhookUrl))
            {
                return;
            }

            List <Field> fields = new List <Field>();

            string[] array = webhook.MessageFormat.Split(new string[] { ": ", ", " }, StringSplitOptions.RemoveEmptyEntries);
            int      num   = 0;

            while (num < array.Length - 1)
            {
                string[] arr = array.Skip(num).Take(2).ToArray();

                string value = arr[1].Replace("{name}", args[0]).Replace("{steamid}", args[1]).Replace("{punisher}", args[2]);

                if (messageType != EMessageType.Unban)
                {
                    value = value.Replace("{reason}", args[3]);
                }
                if (messageType == EMessageType.Ban)
                {
                    value = value.Replace("{duration}", args[4]);
                }

                Field field = new Field(arr[0], value, true);

                fields.Add(field);
                num += 2;
            }

            DiscordWebhookMessage msg = new DiscordWebhookMessage();

            msg.embeds = new List <Embed>()
            {
                new Embed(fields)
            };

            using (WebClient wc = new WebClient())
            {
                wc.Headers.Add(HttpRequestHeader.ContentType, "application/json");
                wc.UploadString(webhook.WebhookUrl, JsonConvert.SerializeObject(msg));
            }
        }
Exemple #11
0
        private async void PostWebhookMessage(DiscordWebhookMessage message, IServerConsole console)
        {
            if (config.WebhookUrl == null)
            {
                return;
            }

            try
            {
                await client.PostAsync(config.WebhookUrl, new StringContent(message.ToString(), Encoding.UTF8, "application/json"));
            }
            catch (Exception ex)
            {
                console.DisplayLine($"[Discord Webhook] Error sending webhook message: {ex}", Color.Red);
            }
        }
Exemple #12
0
        private void OnQuestAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <QuestData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Quest Found [Alarm: {e.Alarm.Name}, PokestopId: {e.Data.PokestopId}, Type={e.Data.Type}]");

            var quest = e.Data;
            var loc   = GeofenceService.GetGeofence(e.Alarm.GeofenceItems, new Location(quest.Latitude, quest.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[QUEST] Failed to lookup city for coordinates {quest.Latitude},{quest.Longitude}, skipping...");
                return;
            }

            if (!_servers.ContainsKey(e.GuildId))
            {
                return;
            }

            if (!_whConfig.Instance.Servers.ContainsKey(e.GuildId))
            {
                return;
            }

            try
            {
                var client    = _servers[e.GuildId];
                var eb        = quest.GenerateQuestMessage(e.GuildId, client, _whConfig.Instance, e.Alarm, loc?.Name ?? e.Alarm.Name);
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = eb.Username,
                    AvatarUrl = eb.IconUrl,
                    Content   = eb.Description,
                    Embeds    = eb.Embeds
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);
                Statistics.Instance.QuestAlarmsSent++;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #13
0
        private void OnRaidAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <RaidData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Raid Found [Alarm: {e.Alarm.Name}, Raid: {e.Data.PokemonId}, Level: {e.Data.Level}, StartTime: {e.Data.StartTime}]");

            var raid = e.Data;
            var loc  = _whm.GeofenceService.GetGeofence(e.Alarm.Geofences, new Location(raid.Latitude, raid.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[RAID] Failed to lookup city from coordinates {raid.Latitude},{raid.Longitude} {pkmn.Name} {raid.Level}, skipping...");
                return;
            }

            try
            {
                var pkmn      = Database.Instance.Pokemon[raid.PokemonId];
                var form      = raid.PokemonId.GetPokemonForm(raid.Form.ToString());
                var pkmnImage = raid.IsEgg ? string.Format(_whConfig.Urls.EggImage, raid.Level) : raid.PokemonId.GetPokemonImage(_whConfig.Urls.PokemonImage, PokemonGender.Unset, raid.Form);
                var eb        = raid.GenerateRaidMessage(_client, _whConfig, e.Alarm, loc.Name, pkmnImage);
                var name      = raid.IsEgg ? $"Level {raid.Level} {pkmn.Name}" : $"{(string.IsNullOrEmpty(form) ? null : form + "-")}{pkmn.Name} Raid";
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = name,
                    AvatarUrl = pkmnImage,
                    Embeds    = new List <DiscordEmbed> {
                        eb
                    }
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);

                Statistics.Instance.RaidsSent++;
                if (raid.PokemonId > 0)
                {
                    Statistics.Instance.IncrementRaidStats(raid.PokemonId);
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
        public void SendMessage(string content, EMessageType messageType)
        {
            DiscordWebhook webhook = configuration.Webhooks.FirstOrDefault(x => x.WebhookType == messageType.ToString());

            if (webhook == null || string.IsNullOrEmpty(webhook.WebhookUrl))
            {
                return;
            }

            DiscordWebhookMessage msg = new DiscordWebhookMessage();

            msg.embeds = new List <Embed>()
            {
                new Embed(content, Convert.ToInt32(webhook.WebhookColor, 16))
            };

            using (WebClient wc = new WebClient())
            {
                wc.Headers.Add(HttpRequestHeader.ContentType, "application/json");
                wc.UploadString(webhook.WebhookUrl, JsonConvert.SerializeObject(msg));
            }
        }
Exemple #15
0
        private void OnQuestAlarmTriggered(object sender, AlarmEventTriggeredEventArgs <QuestData> e)
        {
            if (string.IsNullOrEmpty(e.Alarm.Webhook))
            {
                return;
            }

            _logger.Info($"Quest Found [Alarm: {e.Alarm.Name}, PokestopId: {e.Data.PokestopId}, Type={e.Data.Type}]");

            var quest = e.Data;
            var loc   = _whm.GeofenceService.GetGeofence(e.Alarm.Geofences, new Location(quest.Latitude, quest.Longitude));

            if (loc == null)
            {
                //_logger.Warn($"[QUEST] Failed to lookup city for coordinates {quest.Latitude},{quest.Longitude}, skipping...");
                return;
            }

            try
            {
                var eb        = quest.GenerateQuestMessage(_client, _whConfig, e.Alarm, loc?.Name ?? e.Alarm.Name);
                var jsonEmbed = new DiscordWebhookMessage
                {
                    Username  = quest.GetQuestMessage(),
                    AvatarUrl = quest.GetIconUrl(_whConfig),
                    Embeds    = new List <DiscordEmbed> {
                        eb
                    }
                }.Build();
                NetUtil.SendWebhook(e.Alarm.Webhook, jsonEmbed);

                Statistics.Instance.QuestsSent++;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
            }
        }
Exemple #16
0
 public ParsedItem(string url, DiscordWebhookMessage parsedMessage)
 {
     Url           = url;
     ParsedMessage = parsedMessage;
 }
Exemple #17
0
 public void OnPlayerDisconnect(IServerConsole console, ServerConnectionMessage message)
 {
     PostWebhookMessage(DiscordWebhookMessage.FromServerMessage(message), console);
 }
 public static void Send(this IDiscordWebhookClient client, DiscordWebhookMessage message)
 {
     client.SendAsync(message).GetAwaiter().GetResult();
 }