diff --git a/sites/chada.ma/chada.ma.channels.xml b/sites/chada.ma/chada.ma.channels.xml
new file mode 100644
index 00000000..74030163
--- /dev/null
+++ b/sites/chada.ma/chada.ma.channels.xml
@@ -0,0 +1,4 @@
+
+
+ Chada TV
+
diff --git a/sites/chada.ma/chada.ma.config.js b/sites/chada.ma/chada.ma.config.js
new file mode 100644
index 00000000..087028a7
--- /dev/null
+++ b/sites/chada.ma/chada.ma.config.js
@@ -0,0 +1,54 @@
+const axios = require('axios');
+const cheerio = require('cheerio');
+const dayjs = require('dayjs');
+const utc = require('dayjs/plugin/utc');
+const timezone = require('dayjs/plugin/timezone');
+const customParseFormat = require('dayjs/plugin/customParseFormat');
+
+dayjs.extend(utc);
+dayjs.extend(timezone);
+dayjs.extend(customParseFormat);
+
+module.exports = {
+ site: 'chada.ma',
+ channels: 'chada.ma.channels.xml',
+ days: 1,
+ request: {
+ cache: {
+ ttl: 60 * 60 * 1000 // 1 hour
+ }
+ },
+ url() {
+ return 'https://chada.ma/fr/chada-tv/grille-tv/';
+ },
+ parser: function ({ content }) {
+ const $ = cheerio.load(content);
+ const programs = [];
+
+ $('#stopfix .posts-area h2').each((i, element) => {
+ const timeRange = $(element).text().trim();
+ const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()));
+
+ const titleElement = $(element).next('div').next('h3');
+ const title = titleElement.text().trim();
+
+ const description = titleElement.next('div').text().trim() || 'No description available';
+
+ programs.push({
+ title,
+ description,
+ start,
+ stop
+ });
+ });
+
+ return programs;
+ }
+};
+
+function parseProgramTime(timeStr) {
+ const timeZone = 'Africa/Casablanca';
+ const currentDate = dayjs().format('YYYY-MM-DD');
+
+ return dayjs.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone).format('YYYY-MM-DDTHH:mm:ssZ');
+}
diff --git a/sites/chada.ma/chada.ma.test.js b/sites/chada.ma/chada.ma.test.js
new file mode 100644
index 00000000..557da6ea
--- /dev/null
+++ b/sites/chada.ma/chada.ma.test.js
@@ -0,0 +1,60 @@
+const { parser, url } = require('./chada.ma.config.js')
+const axios = require('axios')
+const dayjs = require('dayjs')
+const cheerio = require('cheerio')
+const utc = require('dayjs/plugin/utc')
+const timezone = require('dayjs/plugin/timezone')
+const customParseFormat = require('dayjs/plugin/customParseFormat')
+
+dayjs.extend(utc)
+dayjs.extend(timezone)
+dayjs.extend(customParseFormat)
+
+jest.mock('axios')
+
+const mockHtmlContent = `
+
+
Programmes d'Aujourd'hui
+
+
00:00 - 09:00
+
+
Bloc Prime + Clips
+
+
+
+
+`;
+
+it('can generate valid url', () => {
+ expect(url()).toBe('https://chada.ma/fr/chada-tv/grille-tv/')
+});
+
+it('can parse response', () => {
+ const content = mockHtmlContent
+
+ const result = parser({ content }).map(p => {
+ p.start = dayjs(p.start).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
+ p.stop = dayjs(p.stop).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
+ return p
+ })
+
+ expect(result).toMatchObject([
+ {
+ title: "Bloc Prime + Clips",
+ description: "No description available",
+ start: dayjs.tz('00:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ'),
+ stop: dayjs.tz('09:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
+ }
+ ])
+})
+
+it('can handle empty guide', () => {
+ const result = parser({
+ content: ''
+ })
+ expect(result).toMatchObject([])
+})
diff --git a/sites/chada.ma/readme.md b/sites/chada.ma/readme.md
new file mode 100644
index 00000000..a4fb2175
--- /dev/null
+++ b/sites/chada.ma/readme.md
@@ -0,0 +1,13 @@
+# chada.ma
+
+https://chada.ma/fr/chada-tv/grille-tv/
+
+### Download the guide
+
+```sh
+npm run grab --- --site=chada.ma
+```
+
+### Test
+
+npm test --- chada.ma