';
}
// Same .tc-room skeleton as renderRoomRow (avatar/info/top/bottom) so a group row matches the
// 1:1 rows visually — data-type="group" + data-gid instead of data-cid/data-uid is what the
// click-delegation in renderRooms() switches on. No online/typing indicator (doesn't apply to
// a group), and the avatar is a rounded square instead of a circle as the only visual cue
// distinguishing a group from a person, mirroring common chat-app convention.
function renderGroupRow(group) {
var gid = group.gid;
var name = group.name || '';
var lastMsg = group.lastMessage;
var preview = '';
if (lastMsg) {
preview = parseDisplayText(lastMsg.msm);
if (!preview) {
if (lastMsg.urlima && lastMsg.urlima.indexOf('public://') >= 0) preview = '📷';
else if (lastMsg.urlaudi && lastMsg.urlaudi.indexOf('public://') >= 0) preview = '🎵';
}
if (String(lastMsg.whosend) === String(userId)) preview = 'You: ' + preview;
} else {
preview = 'No messages yet';
}
var timeStr = group.updated ? fmtTime(unixToDbTimestamp(group.updated)) : '';
var unread = group.unreadCount > 0;
var active = activeGid === gid;
return '
';
}
var body = '';
if (msg.urlima === '_loading_') {
body += '
📷 Sending…
';
} else if (msg.urlima && msg.urlima.indexOf('public://') >= 0) {
var src = msg.urlima.replace('public://', FILES_BASE);
body += '';
}
if (msg.urlaudi && msg.urlaudi.indexOf('public://') >= 0) {
var asrc = msg.urlaudi.replace('public://', FILES_BASE);
body += '';
}
if (meta && meta.type === 'loc') {
body += locationLinkHtml(meta.lat, meta.lng);
} else if (!isSalt && text) {
body += '
' + linkifyText(escHtml(text)) + '
';
}
var time = fmtTime(msg.timestamp || '');
return '
'
+ metaHtml + body
+ '' + escHtml(time) + ''
+ '
';
}
// ── Render group messages ────────────────────────────────────────────────────
function renderGroupMessages(gid) {
var msgs = allGroupMsgs[gid] || [];
var $el = $('#tc-msgs');
var order = msgs.slice().reverse();
$el.html(order.map(renderGroupMsg).join(''));
$el.scrollTop($el[0].scrollHeight);
}
// Same shape as renderMsg(), plus a sender-name label on incoming bubbles — unlike a 1:1
// conversation, "not me" doesn't tell you who actually sent it. Reuses the identical
// urlima/urlaudi rendering, so images/audio sent from the Android app (or sendGroupImage()
// here) display correctly. Audio still has no send control in this web widget.
function renderGroupMsg(msg) {
var isMe = String(msg.whosend) === String(userId);
var side = isMe ? 'tc-out' : 'tc-in';
var parsed = parseInlineMeta(msg.msm || '');
var text = parsed.text;
var meta = parsed.meta;
var isSalt = (text.length === 18 && text.indexOf(' ') < 0);
var senderHtml = '';
if (!isMe) {
senderHtml = '
' + escHtml(msg.senderName || msg.whosend) + '
';
}
var metaHtml = '';
if (meta && meta.type === 'fwd') {
metaHtml = '
';
}
// ── Send (opc 11) ────────────────────────────────────────────────────────────
function sendMessage() {
var text = $.trim($('#tc-input').val());
if (!text || !activeCid) return;
$('#tc-input').val('').focus();
isTyping = false;
post({ opc: '11', userid: userId, cid: activeCid, msm: text, fid: '0' })
.then(function (resp) {
if (!resp) return;
var ok = (resp.resp === 3 || resp.resp === '3' ||
resp.resp === 11 || resp.resp === '11');
if (!ok) return;
var msg = {
id: resp.lastmid || String(Date.now()),
whosend: String(userId),
msm: text,
urlima: '',
urlaudi: '',
timestamp: new Date().toISOString().replace('T', ' ').substr(0, 19)
};
if (!allMsgs[activeCid]) allMsgs[activeCid] = [];
allMsgs[activeCid].unshift(msg);
renderMessages(activeCid);
if (socket && socket.connected) {
socket.emit('send-message', {
id: resp.lastmid,
chatId: activeCid,
from: String(userId),
to: String(activeUid),
message: text,
type: 'text'
});
}
});
}
// ── Send group message ───────────────────────────────────────────────────────
// Purely socket-based — group-message-send both persists (tc_group_message, via the
// signaling server) and delivers, unlike 1:1's HTTP-then-socket-notify split. The server
// echoes the message back to the sender too (see group-message-send in signaling-server-2.js),
// so onGroupSocketMessage() is what actually appends it here — no optimistic local insert.
function sendGroupMessage() {
var text = $.trim($('#tc-input').val());
if (!text || !activeGid) return;
if (!socket || !socket.connected) return;
$('#tc-input').val('').focus();
isTyping = false;
socket.emit('group-message-send', {
gid: parseInt(activeGid, 10),
from: userId,
message: text,
type: 'text',
localId: 'web-' + Date.now() + '-' + Math.random().toString(36).substr(2, 8)
});
}
// ── Send group image (group-message-send, type:"image") ─────────────────────
// Unlike 1:1 (HTTP upload then socket notify), groups have no HTTP upload endpoint —
// the server expects the raw file as a base64 string on the same socket event used for
// text (see group-message-send in signaling-server-2.js), and does its own resize/compress
// (sharp, 1024x1024 max, jpeg75) server-side before writing it and echoing "group-message"
// back to every member including the sender. That echo is what onGroupSocketMessage() uses
// to both confirm this optimistic placeholder (matched by localId) and actually display it.
function sendGroupImage(file) {
if (!activeGid) return;
if (!socket || !socket.connected) return;
if (pendingGroupImage) return; // one at a time — button is disabled meanwhile anyway
var gid = activeGid;
var localId = 'web-img-' + Date.now() + '-' + Math.random().toString(36).substr(2, 8);
pendingGroupImage = { gid: gid, localId: localId };
if (!allGroupMsgs[gid]) allGroupMsgs[gid] = [];
allGroupMsgs[gid].unshift({
id: localId, localId: localId, whosend: String(userId), senderName: myName,
msm: '', urlima: '_loading_', urlaudi: '', created: Math.floor(Date.now() / 1000)
});
renderGroupMessages(gid);
$('#tc-img-btn').attr('disabled', 'disabled');
// Safety net — if the server/socket never answers at all (disconnect mid-flight etc.),
// don't leave "Sending…" stuck forever (see the 1:1 image-send bug this mirrors).
var timeoutId = setTimeout(function () { onGroupMessageError({}); }, 20000);
compressImageIfNeeded(file, function (uploadFile) {
var reader = new FileReader();
reader.onload = function () {
clearTimeout(timeoutId);
if (!pendingGroupImage || pendingGroupImage.localId !== localId) return; // already cleared
var base64 = (reader.result || '').split(',')[1] || '';
if (!base64 || !socket || !socket.connected) { onGroupMessageError({}); return; }
socket.emit('group-message-send', {
gid: parseInt(gid, 10),
from: userId,
message: '',
type: 'image',
filedata: base64,
localId: localId
});
};
reader.onerror = function () {
clearTimeout(timeoutId);
onGroupMessageError({});
};
reader.readAsDataURL(uploadFile);
});
}
// Clears whatever group image send is currently in flight — used both for explicit
// "group-message-error" replies (bad file, not a member, rate-limited) and the
// client-side timeout above. Doesn't distinguish by localId: only one send is ever
// in flight at a time (the button is disabled meanwhile), so there's nothing to match.
function onGroupMessageError(data) {
if (!pendingGroupImage) return;
var gid = pendingGroupImage.gid;
var localId = pendingGroupImage.localId;
pendingGroupImage = null;
$('#tc-img-btn').removeAttr('disabled');
if (allGroupMsgs[gid]) {
allGroupMsgs[gid] = allGroupMsgs[gid].filter(function (m) { return m.localId !== localId; });
}
if (activeGid === gid && isOpen) renderGroupMessages(gid);
if (data && data.error) { console.warn('group image send failed:', data.error); }
}
// Resizes/re-encodes as JPEG when a file exceeds COMPRESS_THRESHOLD. Skips GIFs —
// redrawing to canvas would flatten an animated GIF to a single static frame.
// Falls back to the original file untouched if decoding/encoding fails for any reason.
function compressImageIfNeeded(file, callback) {
if (file.size <= COMPRESS_THRESHOLD || file.type === 'image/gif') {
callback(file);
return;
}
var img = new Image();
var url = URL.createObjectURL(file);
img.onload = function () {
URL.revokeObjectURL(url);
var scale = Math.min(1, COMPRESS_MAX_DIM / Math.max(img.width, img.height));
var canvas = document.createElement('canvas');
canvas.width = Math.round(img.width * scale);
canvas.height = Math.round(img.height * scale);
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(function (blob) {
callback(blob || file, !!blob);
}, 'image/jpeg', COMPRESS_QUALITY);
};
img.onerror = function () {
URL.revokeObjectURL(url);
callback(file);
};
img.src = url;
}
// ── Send image (opc 11I) ─────────────────────────────────────────────────────
function sendImage(file) {
if (!activeCid) return;
var tempId = '_img_' + Date.now();
if (!allMsgs[activeCid]) allMsgs[activeCid] = [];
allMsgs[activeCid].unshift({
id: tempId, whosend: String(userId), msm: '', urlima: '_loading_',
urlaudi: '', timestamp: new Date().toISOString().replace('T', ' ').substr(0, 19)
});
renderMessages(activeCid);
// .attr()/.removeAttr(), not .prop() — this site's jQuery predates 1.6 (which is
// when .prop() was introduced), and every other call in this file sticks to
// pre-1.6-safe APIs already.
$('#tc-img-btn').attr('disabled', 'disabled');
compressImageIfNeeded(file, function (uploadFile, wasCompressed) {
var form = new FormData();
form.append('opc', '11I');
form.append('userid', String(userId));
form.append('cid', activeCid);
form.append('image', uploadFile, wasCompressed ? 'upload.jpg' : file.name);
fetch(API_URL, { method: 'POST', body: form })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (resp) {
allMsgs[activeCid] = allMsgs[activeCid].filter(function (m) { return m.id !== tempId; });
if (resp && (resp.resp === 3 || resp.resp === '3') && resp.urlima) {
var msg = {
id: resp.lastmid || String(Date.now()),
whosend: String(userId),
msm: '',
urlima: resp.urlima,
urlaudi: '',
timestamp: new Date().toISOString().replace('T', ' ').substr(0, 19)
};
allMsgs[activeCid].unshift(msg);
if (socket && socket.connected) {
socket.emit('send-message', {
id: resp.lastmid,
chatId: activeCid,
from: String(userId),
to: String(activeUid),
message: '',
urlima: resp.urlima,
type: 'image'
});
}
}
renderMessages(activeCid);
})
.catch(function () {
allMsgs[activeCid] = allMsgs[activeCid].filter(function (m) { return m.id !== tempId; });
renderMessages(activeCid);
})
.then(function () { $('#tc-img-btn').removeAttr('disabled'); });
});
}
// ── Inline meta parser ────────────────────────────────────────────────────────
function parseInlineMeta(raw) {
if (!raw) return { text: '', meta: null };
if (raw.substr(0, FWD_MARKER.length) === FWD_MARKER) {
var end = raw.indexOf(META_ETX);
if (end < 0) return { text: raw, meta: null };
var sender = raw.substring(FWD_MARKER.length, end);
return { text: raw.substring(end + 1), meta: { type: 'fwd', sender: sender } };
}
if (raw.substr(0, REPLY_MARKER.length) === REPLY_MARKER) {
var rend = raw.indexOf(META_ETX);
if (rend < 0) return { text: raw, meta: null };
var parts = raw.substring(REPLY_MARKER.length, rend);
var sep = parts.indexOf(META_SEP);
var rsender = sep >= 0 ? parts.substring(0, sep) : parts;
var orig = sep >= 0 ? parts.substring(sep + 1) : '';
return { text: raw.substring(rend + 1), meta: { type: 'reply', sender: rsender, orig: orig } };
}
// Shared location — same "lat" + META_SEP + "lng" encoding the Android app writes
// (see LOC_MARKER / sendLocationMessage() in ActivityChat.java). No trailing text.
if (raw.substr(0, LOC_MARKER.length) === LOC_MARKER) {
var lend = raw.indexOf(META_ETX);
if (lend < 0) return { text: raw, meta: null };
var coords = raw.substring(LOC_MARKER.length, lend);
var lsep = coords.indexOf(META_SEP);
var lat = lsep >= 0 ? coords.substring(0, lsep) : coords;
var lng = lsep >= 0 ? coords.substring(lsep + 1) : '';
return { text: raw.substring(lend + 1), meta: { type: 'loc', lat: lat, lng: lng } };
}
return { text: raw, meta: null };
}
function parseDisplayText(msm) {
var p = parseInlineMeta(msm || '');
var t = p.text;
if (p.meta && p.meta.type === 'loc') return '📍 ' + LOC_LABEL;
if (t.length === 18 && t.indexOf(' ') < 0) return '';
return (p.meta ? (p.meta.type === 'fwd' ? '↪ ' : '↩ ') : '') + t;
}
// ── Badge & notifications ─────────────────────────────────────────────────────
function updateBadge() {
var n = Object.keys(unreadCids).length;
Object.keys(allGroups).forEach(function (k) {
if (allGroups[k].unreadCount > 0) n++;
});
var $b = $('#tc-badge');
if (n > 0) { $b.text(n).removeClass('tc-hidden'); }
else { $b.addClass('tc-hidden'); }
}
function showNotif(title, body) {
if (!('Notification' in window)) return;
if (Notification.permission === 'granted') {
new Notification(title, {
body: body || '',
icon: 'https://www.taleoi.com/sites/default/files/taleoi.ico'
});
} else if (Notification.permission === 'default') {
Notification.requestPermission();
}
}
// ── HTTP ──────────────────────────────────────────────────────────────────────
function post(params) {
var form = new FormData();
Object.keys(params).forEach(function (k) { form.append(k, String(params[k])); });
return fetch(API_URL, { method: 'POST', body: form })
.then(function (r) { return r.ok ? r.json() : null; })
.catch(function () { return null; });
}
// ── Helpers ───────────────────────────────────────────────────────────────────
// Group timestamps (tc_group.updated, tc_group_message.created) are unix seconds, unlike
// 1:1's "YYYY-MM-DD HH:MM:SS" strings — converts to that same shape (local time) so both can
// share fmtTime() and the plain string comparison renderRooms() sorts by.
// Formatted with UTC getters, not local — fmtTime() (below) always treats this
// "YYYY-MM-DD HH:MM:SS" shape as UTC (appending 'Z' before parsing), matching the raw
// DB timestamp strings 1:1 chat passes it directly. Using local getters here would hand
// fmtTime() an already-local time it then re-shifts by the viewer's UTC offset again.
function unixToDbTimestamp(unixSec) {
if (!unixSec) return '0';
var d = new Date(unixSec * 1000);
function pad(n) { return (n < 10 ? '0' : '') + n; }
return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + ' '
+ pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds());
}
function fmtTime(ts) {
if (!ts) return '';
// DB timestamps are plain "YYYY-MM-DD HH:MM:SS" in UTC (MySQL TIMESTAMP column).
// Without a trailing 'Z', the JS Date parser treats a date-TIME string (unlike a
// date-only string) as local time, not UTC — silently shifting displayed times by
// the viewer's UTC offset. The 'Z' forces correct UTC interpretation.
var d = new Date(ts.replace(' ', 'T') + 'Z');
if (isNaN(d.getTime())) return '';
var now = new Date();
if (d.toDateString() === now.toDateString()) {
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
return (d.getMonth() + 1) + '/' + d.getDate();
}
function escHtml(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&').replace(//g, '>')
.replace(/"/g, '"').replace(/'/g, ''');
}
function escAttr(s) { return escHtml(s); }
// Wraps http(s) URLs in a message's already-escaped text with clickable links. Must run
// AFTER escHtml() — matching against the escaped string is safe since escaping never
// introduces whitespace inside a URL run, and the entities it does introduce (e.g. "&"
// for a literal "&" in a query string) are exactly what a browser expects inside an href
// attribute anyway. target="_blank" + rel="noopener noreferrer" so following a link never
// navigates away from (or exposes window.opener to) the page the chat widget is floating on.
function linkifyText(escapedText) {
return escapedText.replace(/https?:\/\/[^\s<]+/g, function (url) {
var trail = '';
var m = url.match(/[.,!?:;)\]}'"]+$/);
if (m) { trail = m[0]; url = url.slice(0, -trail.length); }
if (!url) return trail;
return '' + url + '' + trail;
});
}
// A shared location (LOC_MARKER — see parseInlineMeta()) renders as a tappable card
// linking to Google Maps, matching how the Android app opens a geo: intent for the
// same coordinates. Uses an https:// maps URL instead since geo: isn't reliably
// clickable in desktop browsers.
function locationLinkHtml(lat, lng) {
var mapUrl = 'https://www.google.com/maps?q=' + encodeURIComponent(lat) + ',' + encodeURIComponent(lng);
return ''
+ '📍 ' + escHtml(LOC_LABEL) + '';
}
})(jQuery);
world taleoi | taleoi.com
NEM José Eustasio Rivera: una historia de educación y transformación en el Amazonas
Google
En el corazón de Leticia, Amazonas, se encuentra una institución que durante décadas ha acompañado la formación de generaciones de jóvenes: la Institución Educativa INEM José Eustasio Rivera.
El INEM de Leticia nació en el contexto de la po...read more
En el corazón de Leticia, Amazonas, se encuentra una institución que durante décadas ha acompañado la formación de generaciones de jóvenes: la Institución Educativa INEM José Eustasio Rivera.
El INEM de Leticia nació en el contexto de la política colombiana de Educación Media Diversificada. Su creación fue establecida en 1980, como parte de una estrategia educativa que buscaba ofrecer a los estudiantes una formación más amplia, combinando la educación académica con diferentes áreas de preparación para la vida profesional y laboral.
La institución inició sus labores académicas el 16 de abril de 1980, con aproximadamente 200 estudiantes y 22 profesores. Cuatro años después, en 1984, entregó su primera promoción de bachilleres: 44 estudiantes, formados en las modalidades de Ciencias Naturales, Industrial y Comercio.
Desde sus primeros años, el INEM representó una apuesta importante para la educación de la Amazonia. Su llegada permitió ampliar las posibilidades de formación de los jóvenes leticianos y respondió también a la necesidad de fortalecer la presencia educativa del Estado en una región estratégica de Colombia.
Con el paso de los años, la institución fue transformándose. En 2002 incorporó la educación de adultos en jornada nocturna y posteriormente se integró la Sede Once de Noviembre, ampliando su cobertura educativa.
Actualmente, la Institución Educativa INEM José Eustasio Rivera es un establecimiento oficial, mixto y urbano de Leticia. Cuenta con la sede principal y la sede Once de Noviembre, y ofrece formación desde preescolar hasta educación media, además de programas para población adulta.
Más que un colegio, el INEM forma parte de la memoria educativa de Leticia. Por sus aulas han pasado generaciones de amazonenses que hoy hacen parte de diferentes sectores de la vida social, cultural, comercial y profesional de la región.
Después de más de cuatro décadas, su historia continúa ligada a la transformación de la educación en el Amazonas y al desafío permanente de formar nuevas generaciones desde una realidad profundamente amazónica.
IINEM: educación ambiental que se convierte en acción
NEM: Educación ambiental que se convierte en acción
El compromiso con el cuidado del medio ambiente también se construye desde las aulas. En la Institución Educativa INEM José Eustasio Rivera de Leticia, los estudiantes han participado en i...read more
NEM: Educación ambiental que se convierte en acción
El compromiso con el cuidado del medio ambiente también se construye desde las aulas. En la Institución Educativa INEM José Eustasio Rivera de Leticia, los estudiantes han participado en iniciativas que buscan fortalecer la cultura del reciclaje y el manejo responsable de los residuos.
Una de estas experiencias se desarrolló el pasado 28 de mayo, cuando estudiantes y docentes se vincularon a la estrategia Ruta Selectiva, liderada por Corpoamazonia, participando en la recolección, clasificación, empaque y entrega de botellas plásticas.
Más allá de una jornada puntual, estas acciones reflejan la importancia de formar estudiantes conscientes de su entorno y capaces de aportar, desde pequeñas acciones, a la protección de la Amazonia.
Porque educar también es enseñar a cuidar el territorio. 🌿
Policía Nacional captura en Leticia a alias “Mariano” e incauta 400 gramos de base de coca
Policía Nacional captura en Leticia a alias “Mariano” e incauta 400 gramos de base de coca
Leticia, Amazonas. En el marco de las acciones adelantadas contra el tráfico de estupefacientes, la Policía Nacional de Colombia, a través de las Seccionales de Investigación Criminal e Inteligencia Policial, en coordinación con la Fiscalía Primer...read more
Leticia, Amazonas. En el marco de las acciones adelantadas contra el tráfico de estupefacientes, la Policía Nacional de Colombia, a través de las Seccionales de Investigación Criminal e Inteligencia Policial, en coordinación con la Fiscalía Primera Seccional y la Fuerza Aeroespacial Colombiana, realizó una diligencia de registro y allanamiento que permitió la captura en flagrancia de un hombre conocido con el alias de “Mariano”.
El procedimiento, desarrollado en la ciudad de Leticia, permitió además la incautación de aproximadamente 400 gramos de base de coca, así como de elementos presuntamente relacionados con esta actividad ilícita.
Resultados operativos
1 persona capturada en flagrancia.
400 gramos de base de coca incautados.
1 gramera incautada.
2 teléfonos celulares incautados.
El capturado y los elementos incautados fueron dejados a disposición de la autoridad competente, que continuará con el proceso judicial correspondiente.
Con este resultado, la Policía Nacional reafirma su compromiso con la seguridad y la convivencia ciudadana, fortaleciendo las acciones operativas y de investigación orientadas a contrarrestar el tráfico de estupefacientes en el departamento del Amazonas.
La institución invita a la comunidad a denunciar oportunamente cualquier hecho que pueda afectar la seguridad y convivencia ciudadana a través de la línea 123.
Con mucho entusiasmo queremos compartir parte de la experiencia vivida por nuestros compañeros en el Centro de Interés ExpresArte, en las líneas de dibujo técnico y pintura al óleo.
Durante esta primera etapa, nuestros estudiantes tuvieron ...read more
Con mucho entusiasmo queremos compartir parte de la experiencia vivida por nuestros compañeros en el Centro de Interés ExpresArte, en las líneas de dibujo técnico y pintura al óleo.
Durante esta primera etapa, nuestros estudiantes tuvieron la oportunidad de explorar el dibujo del rostro humano, desarrollar su creatividad y expresar emociones a través del arte. Como resultado, cada participante realizó el boceto de su autorretrato y su transferencia al lienzo, convirtiendo cada obra en una muestra de identidad, talento y dedicación. 🎨✨
Desde el Personero Estudiantil y el Consejo Estudiantil, queremos reconocer y felicitar a todos nuestros compañeros por su compromiso, creatividad y entusiasmo.
El arte nos permite expresarnos, descubrir nuestros talentos y construir juntos una comunidad estudiantil más creativa.
👏 ¡Felicitaciones a nuestros artistas! Ahora esperamos con entusiasmo la siguiente etapa, donde el color y la técnica al óleo darán vida a cada creación.
Personero Estudiantil y Consejo Estudiantil
INEM José Eustasio Rivera – Leticia, Amazonas
Policía Nacional socializa la Ley Ángel a través de espacio radial en Leticia
Policía Nacional socializa la Ley Ángel a través de espacio radial en Leticia
Leticia, Amazonas. En el marco de las estrategias de prevención y acercamiento con la comunidad, la Seccional de Carabineros y Protección Ambiental de la Policía Nacional participó en el espacio radial “Cuéntele al Comandante”, emitido por Radio P...read more
Leticia, Amazonas. En el marco de las estrategias de prevención y acercamiento con la comunidad, la Seccional de Carabineros y Protección Ambiental de la Policía Nacional participó en el espacio radial “Cuéntele al Comandante”, emitido por Radio Policía Leticia 98.9 FM.
Durante este encuentro con la comunidad se socializaron los principales aspectos de la Ley Ángel (Ley 2455 de 2025), normativa que fortalece las medidas de prevención, atención y sanción frente al maltrato animal, promoviendo el respeto, el cuidado y la protección de los animales.
La jornada permitió brindar información a los ciudadanos sobre la importancia de prevenir cualquier forma de violencia o maltrato hacia los animales, así como fortalecer la cultura de protección y bienestar animal en el territorio.
A través de estos espacios de comunicación, la Policía Nacional continúa promoviendo el diálogo con la comunidad, generando escenarios de participación y educación que contribuyen a fortalecer la convivencia ciudadana y la protección de los animales.
En el marco del 31 de agosto, Día Internacional de los Afrodescendientes, la Policía Nacional reafirma su compromiso con el respeto por la diversidad cultural.
En el marco del 31 de agosto, Día Internacional de los Afrodescendientes, la Policía Nacional reafirma su compromiso con el respeto por la diversidad cultural.
En el marco del 31 de agosto, Día Internacional de los Afrodescendientes, la Policía Nacional reafirma su compromiso con el respeto por la diversidad cultural, la inclusión y la garantía de los derechos de las comunidades afrodescendientes.
En el marco del 31 de agosto, Día Internacional de los Afrodescendientes, la Policía Nacional reafirma su compromiso con el respeto por la diversidad cultural, la inclusión y la garantía de los derechos de las comunidades afrodescendientes.
Esta fecha representa una oportunidad para reconocer los aportes históricos, culturales y sociales de las comunidades afrodescendientes, así como para promover espacios de respeto, igualdad y convivencia, libres de cualquier forma de discriminación.
Desde la Policía Nacional se fortalecen acciones de acercamiento y diálogo con las comunidades, promoviendo una convivencia basada en el reconocimiento de la diversidad y el respeto por las diferencias.
La institución invita a la ciudadanía a continuar construyendo una sociedad incluyente, donde la diversidad cultural sea reconocida como una fortaleza y donde todos podamos convivir con respeto, dignidad y equidad.