Home System Support

System Support

By System Admin
6 articles

setup adsense txt on squarespace

To set up an AdSense ads.txt file on Squarespace, you'll need to download the file from Google AdSense, create a blank page on your Squarespace site, upload the ads.txt file to that page, and then use URL mappings to redirect the ads.txt file to the root of your domain. Here's a more detailed breakdown: 1. Download the ads.txt file from AdSense: - Log into your AdSense account. - Navigate to the "Sites" section and locate your Squarespace website. - Download the ads.txt file associated with your site. 1. Create a blank page on Squarespace: - Go to "Pages" in your Squarespace dashboard. - Create a new page under the "Not Linked" section. - You can leave this page blank, as it will only be used for the file upload. 1. Upload the ads.txt file: - On the blank page, add a text block and write any text you want (e.g., "ads.txt"). - Highlight the text and click the hyperlink icon. - Click the settings icon (gear icon). - Navigate to the "File" section. - Upload the ads.txt file you downloaded from AdSense. - Select the uploaded ads.txt file and click "Apply". - Save the page. 4. Use URL mappings: Go to your page's dashboard, Navigate to "Settings" > "Advanced" > "URL Mappings", Add the following URL mapping: /ads.txt -> /s/ads.txt 301, and Click "Save". 5. Test the ads.txt file: - You can access the file at https://your-domain.com/ads.txt. - Google's tools can also be used to check the file. Important Notes: - Ensure your ads.txt file is formatted correctly as specified by the IAB Tech Lab. - You can delete the blank page you created earlier, as the file will still be saved on Squarespace. - If you encounter any issues, consider contacting Squarespace Support or Google AdSense support for assistance.

Last updated on Aug 19, 2025

Nestict.com Linktree

Forms - CBC Leaving Form - NPS Medical Examination Form - Vendor Information Form JSS Exams Grade 7 - Grade 7 Creative Arts & Sports Questions - Grade 7 Christian Religious Education Questions - Grade 7 English Questions - Grade 7 Kiswahili Marking Scheme - Grade 7 Kiswahili Questions - Grade 7 Social Studies Marking Scheme Grade 8 - Grade 8 Agriculture Questions - Grade 8 English Marking Scheme - Grade 8 English Questions - Grade 8 Integrated Science Marking Scheme - Grade 8 Integrated Science Questions - Grade 8 Kiswahili Marking Scheme - Grade 8 Mathematics Questions - Grade 8 Pre-Technical Studies Marking Scheme JSS Resources - JSS Resources (Multiple Pages) - JSS Resources - Page 1 - JSS Resources - Page 2 - JSS Resources - Page 3 - Grade 7 Pre-Technical Studies Schemes of Work (Term 1) - Grade 9 Agriculture & Nutrition Teacher's Guide - Grade 9 Agriculture & Nutrition Lesson Plan - Bridges Without Rivers Guide - Grade 9 Creative Arts & Sports Lesson Plan - Grade 9 CRE Lesson Plan - Grade 9 CRE Lesson Plan (Duplicate?) - Daughter of Nature Guidebook - Grade 8 English Lesson Plan - Grade 4 Creative Arts & Sports - Grade 7 Pre-Technical Studies - Grade 6 Term 1 English Schemes of Work - Grade 7 Creative Arts & Sports Scheme of Work - Grade 7 Maths Simplified Notes - Grade 8 Creative Arts & Sports Complete Notes - Grade 8 Maths Simplified Notes - Grade 8 Term 1 Opener Entry Exams - Grade 9 Creative Arts & Sports Scheme of Work - Grade 9 CRE Schemes of Work (Term 1) - Grade 9 Creative Arts & Sports Schemes of Work (Term 1)

Last updated on Aug 19, 2025

Turning Code Into Cuisine: 14K Recipes Scraped and Served

o get started, let’s load all our necessary libraries: library(RSelenium) library(polite) library(purrr) library(tidyverse) library(mongolite) library(wdman) library(netstat) library(rvest) library(progressr) Now that we have the tools we need on the ready, let’s set up our session using Firefox (or chrome, refer to the docs). The code chunk below will start a Firefox session and set up your client. rD <- rsDriver(browser = "firefox", chromever = NULL, phantomver = NULL, port = free_port()) remDr <- rsDriver$client Great! Now let’s navigate to one of the recipes on the site, which is shows you how to go about making some simple and easy stuffed peppers. remDr$navigate("https://www.allrecipes.com/recipe/105016/simple-and-easy-stuffed-peppers/") page <- read_html(remDr$getPageSource()[[1]]) This reads the HTML source of the current browser page controlled by Selenium into a rvest-compatible object so we can parse and extract data from it using CSS or XPath selectors. Since we are interested in targeting the elements containing the ingredients, author’s name, when the recipe was updated/published, data involving prep times, cook times and other times, nutrition facts, ratings and reviews data, this is the way to go: # Ingredients ingredients <- page |> html_nodes("ul.mm-recipes-structured-ingredients__list li") |> html_text(trim = TRUE) # Author name author <- page |> html_nodes(".mntl-attribution__item-name, span.comp.mntl-bylines__item.mntl-attribution__item.mntl-attribution__item-name") |> html_text(trim = TRUE) # Published date date <- page |> html_node("div.mntl-attribution__item-date") |> html_text(trim = TRUE) # Recipe metadata labels + values (e.g. servings, prep time) labels <- page |> html_nodes("div.mm-recipes-details__label") |> html_text(trim = TRUE) values <- page |> html_nodes("div.mm-recipes-details__value") |> html_text(trim = TRUE) details <- tibble::tibble(label = labels, value = values) # Nutrition facts nutrition_names <- page |> html_nodes("td.mm-recipes-nutrition-facts-summary__table-cell.text-body-100") |> html_text(trim = TRUE) nutrition_values <- page |> html_nodes("td.mm-recipes-nutrition-facts-summary__table-cell.text-body-100-prominent") |> html_text(trim = TRUE) nutrition <- tibble::tibble(fact = nutrition_names, amount = nutrition_values) # Ratings and Reviews average_rating <- page |> html_node("#mm-recipes-review-bar__rating_1-0") |> html_text(trim = TRUE) total_ratings <- page |> html_node("#mm-recipes-review-bar__rating-count_1-0") |> html_text(trim = TRUE) review_count <- page |> html_node("#mm-recipes-review-bar__comment-count_1-0") |> html_text(trim = TRUE) And to compile all this information into a nice table: meta <- tibble::tibble( author = author, date_published = date, average_rating = average_rating, total_ratings = total_ratings, review_count = review_count ) ingredients_tbl <- tibble::tibble(ingredient = ingredients) nutrition_tbl <- tibble::tibble( fact = nutrition_names, amount = nutrition_values ) details_tbl <- tibble::tibble(label = labels, value = values) # Optionally: pivot wider details_wide <- tidyr::pivot_wider(details_tbl, names_from = label, values_from = value) recipe_data <- list( url = "https://www.allrecipes.com/recipe/105016/simple-and-easy-stuffed-peppers/", author = author, date_published = date, ratings = list( average = average_rating, total = total_ratings, reviews = review_count ), details = details_wide, ingredients = ingredients_tbl, nutrition = nutrition_tbl ) So far so hoot! We have successfully scraped a single recipe but we want to scrape a whole lot more. Let’s navigate to the page that will lead us to thousands more, for mankind must feast. # This will lead to 14k recipes recipes_a_to_z <- "https://www.allrecipes.com/recipes-a-z-6735880#alphabetical-list-z" remDr$navigate(recipes_a_to_z) Phase 1: Building the Recipe Category Index The code chunk above leads to a few 100 recipe categories with each further leading to the actual recipes. Therefore, we need to grab these links and store them. You know the drill now. Fetch page source, read the HTML, grab elements of interest (the recipe category links) and go brrrr. And yes, you need to run this line with the page variable again since we are on a new page. page_source <- remDr$getPageSource()[[1]] page <- read_html(page_source) links <- page |> html_node("#mntl-alphabetical-list_1-0") |> html_nodes("a") The lines above target the alphabetical recipe index block and extract all anchor tags (<a>) which contain category names and links. Then we proceed to build our recipe index using our link variable which will contain a tibble of category names and their URLs for use in the next phase. recipe_index <- tibble::tibble( name = html_text(links, trim = TRUE), link = html_attr(links, "href") ) category_urls <- recipe_index$link Phase 2: Scrape Recipes from Each Category Once we have the category URLs in hand, we will dive into each one and extract the actual recipe links. That’s where the scrape_category_recipes() function comes in. It’s purpose is to navigate to a given category page and then parses out two distinct sets of recipe links: those in a kind of “Featured” section and those in the main recipe list. Here’s the function’s logic: scrape_category_recipes <- function(url) { remDr$navigate(url) Sys.sleep(1.5) # Allow page to fully render page <- read_html(remDr$getPageSource()[[1]]) # "Featured" section featured_anchors <- page |> html_node("#mntl-three-post__inner_1-0") |> html_nodes("a") featured <- tibble::tibble( name = featured_anchors |> html_nodes("span.card__title-text ") |> html_text(trim = TRUE), link = featured_anchors |> html_attr("href") ) |> filter(str_detect(link, "/recipe/|recipe-[0-9]+")) |> mutate(type = "Featured", source_url = url) # "Main" recipe list across multiple divs main_anchors <- page |> html_nodes("div.comp.tax-sc__recirc-list.card-list.mntl-universal-card-list.mntl-document-card-list.mntl-card-list.mntl-block") |> html_nodes("a") main <- tibble::tibble( name = main_anchors |> html_nodes("span.card__title-text ") |> html_text(trim = TRUE), link = main_anchors |> html_attr("href") ) |> filter(str_detect(link, "/recipe/|recipe-[0-9]+")) |> mutate(type = "List", source_url = url) # Unified result bind_rows(featured, main) } Once both sets are scraped, bind them together into a single tibble. This function gets called repeatedly for every category URL using purrr::map_dfr(), which stitches all the results into one big dataframe. But of course, scraping isn’t always clean. I quickly noticed duplicates — same recipe showing up in multiple categories. So I counted how many times each link appeared, filtered for those with more than one occurrence, and then deduplicated the dataset using distinct(). # Loop through all category URLs and bind results results <- purrr::map_dfr(category_urls, scrape_category_recipes) # I spy duplicates dupes <- results |> dplyr::count(link) |> dplyr::filter(n > 1) # So I do the deduping results <- results |> dplyr::distinct(link, .keep_all = TRUE) At this point, we should have a clean, unified list of recipe URLs — each one pointing to a page rich with ingredients, nutrition facts, ratings, and more. Now we finna dig in. Phase 3: Now, we feast. Using the logic we developed earlier with the simple and easy stuffed peppers, we get this handy function. scrape_recipe_details <- function(recipe_url) { remDr$navigate(recipe_url) Sys.sleep(0.5) page <- read_html(remDr$getPageSource()[[1]]) # Author & date author <- page |> html_nodes(".mntl-attribution__item-name, span.comp.mntl-bylines__item.mntl-attribution__item.mntl-attribution__item-name") |> html_text(trim = TRUE) date <- page |> html_node("div.mntl-attribution__item-date") |> html_text(trim = TRUE) # Ratings average_rating <- page |> html_node("#mm-recipes-review-bar__rating_1-0") |> html_text(trim = TRUE) total_ratings <- page |> html_node("#mm-recipes-review-bar__rating-count_1-0") |> html_text(trim = TRUE) review_count <- page |> html_node("#mm-recipes-review-bar__comment-count_1-0") |> html_text(trim = TRUE) # Metadata labels <- page |> html_nodes("div.mm-recipes-details__label") |> html_text(trim = TRUE) values <- page |> html_nodes("div.mm-recipes-details__value") |> html_text(trim = TRUE) details <- tibble::tibble(label = labels, value = values) |> tidyr::pivot_wider(names_from = label, values_from = value) # Ingredients ingredients <- page |> html_nodes("ul.mm-recipes-structured-ingredients__list li") |> html_text(trim = TRUE) # Nutrition nutrition_names <- page |> html_nodes("td.mm-recipes-nutrition-facts-summary__table-cell.text-body-100") |> html_text(trim = TRUE) nutrition_values <- page |> html_nodes("td.mm-recipes-nutrition-facts-summary__table-cell.text-body-100-prominent") |> html_text(trim = TRUE) nutrition <- tibble::tibble(fact = nutrition_names, amount = nutrition_values) # Package as tibble row tibble::tibble( url = recipe_url, author = author, date_published = date, ratings = list(tibble::tibble(avg = average_rating, total = total_ratings, reviews = review_count)), details = list(details), ingredients = list(ingredients), nutrition = list(nutrition) ) } This function will collect our data of interest from each of the 14.4K recipes in the results tibble. To store these recipes, I chose MongoDB. It’s flexible, schema-less, and perfect for storing nested data like ingredients, nutrition facts, and ratings. Using the mongolite package, I connected to my database with: mongo_conn <- mongo(collection = "Recipes", db = "Cook", url = "my_mongo_conn_string") This line establishes a connection to a remote MongoDB cluster, targeting the "Recipes" collection inside the "Cook" database which I had created beforehand. Every time a recipe is scraped, it is inserted directly into this collection as a document. MongoDB handles the nested structure beautifully — ingredients become arrays, nutrition facts become embedded objects, and metadata like prep time or servings slot in without needing a rigid schema. To wrap up the scraping and storage workflow, the final code block orchestrates the entire process of iterating through recipe URLs, scraping each one, and inserting the structured data into MongoDB while providing us with a lil progress bar that tells us how far we are with the process. with_progress({ p <- progressor(steps = length(results$link)) purrr::walk(results$link, function(link) { try({ recipe_df <- scrape_recipe_details(link) mongo_conn$insert(recipe_df) p() }, silent = TRUE) }) }) And that’s about it! Next steps: Export the data from the database either using MongoDB Compass’ GUI or use mongolite to clean it and develop whatever data product out of it. Until next time :))

Last updated on Aug 23, 2025

Running Chatwoot on EasyPanel with WhatsApp Integration – Fixing Common Issues

f you’re looking to set up Chatwoot as your customer messaging platform on EasyPanel, integrating it with WhatsApp Cloud API is a great choice. However, many users run into problems during the setup: garbled text, broken image/file links, and webhook verification errors. This guide walks you through the correct configuration, common pitfalls, and fixes so you can get a smooth Chatwoot + WhatsApp integration. Why Chatwoot + EasyPanel? - EasyPanel lets you deploy and manage Docker apps without needing to memorize long Docker commands. - Chatwoot is an open-source customer engagement suite that supports email, WhatsApp, Facebook, Instagram, Telegram, and more. When you combine the two, you get an easy-to-manage messaging platform with enterprise flexibility. Step 1: Deploy Chatwoot on EasyPanel 1. Log in to your EasyPanel instance. 2. Create a new app → select Docker image → use:chatwoot/chatwoot:latest 3. Set your environment variables (.env) in EasyPanel. At minimum:RAILS_ENV=production FRONTEND_URL=https://chatwoot.yourdomain.com SECRET_KEY_BASE=<generate with openssl rand -hex 64> POSTGRES_HOST=your-postgres POSTGRES_USERNAME=chatwoot POSTGRES_PASSWORD=yourpassword REDIS_URL=redis://your-redis:6379/1 LANG=en_US.UTF-8 ACTIVE_STORAGE_SERVICE=local RAILS_STORAGE_SERVICE_URL=https://chatwoot.yourdomain.com 4. Map storage to persist uploaded files:/app/storage → /var/lib/chatwoot/storage Step 2: Connect Chatwoot with WhatsApp Cloud API 1. Go to Meta Developers. 2. Create a WhatsApp app → generate your WhatsApp Business Account ID, Phone number ID, and Permanent Access Token. 3. In Chatwoot, go to Inboxes → Add Inbox → WhatsApp Cloud API. 4. Fill in: - Phone number ID - WABA ID - Access Token - Verify Token (you define this – must match your .env) Add to .env: WHATSAPP_VERIFY_TOKEN=mysecuretoken Save, and Meta will validate your webhook. Step 3: Fixing Common Errors 🔴 Issue 1: Broken attachment links (http://https/rails/...) Cause: Misconfigured FRONTEND_URL or RAILS_STORAGE_SERVICE_URL. ✅ Fix: FRONTEND_URL=https://chatwoot.yourdomain.com RAILS_STORAGE_SERVICE_URL=https://chatwoot.yourdomain.com Rebuild and redeploy. 🔴 Issue 2: Garbled text like ˆ\tAF Cause: Encoding mismatch (WhatsApp sends UTF-8, but DB or environment isn’t aligned). ✅ Fix: - Ensure your DB is created with utf8mb4_general_ci. - Add to .env:LANG=en_US.UTF-8 - Restart the container. 🔴 Issue 3: hmac_verified:false Cause: Webhook verification token mismatch. ✅ Fix: - In Meta → Webhooks, confirm your Verify Token matches Chatwoot’s .env:WHATSAPP_VERIFY_TOKEN=mysecuretoken - Revalidate the webhook. 🔴 Issue 4: Attachments disappearing after restart Cause: Active Storage is writing to ephemeral container storage. ✅ Fix: - Mount a volume in EasyPanel:/app/storage → /var/lib/chatwoot/storage - Or switch to S3/MinIO:ACTIVE_STORAGE_SERVICE=s3 AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx AWS_REGION=us-east-1 AWS_BUCKET=my-chatwoot-files Step 4: Restart and Test After applying fixes, restart your Chatwoot service in EasyPanel: docker-compose down docker-compose up -d --build Then: - Send a test message from WhatsApp → check if it arrives in Chatwoot. - Send an image/file → confirm it loads correctly. Wrapping Up Running Chatwoot on EasyPanel is straightforward, but WhatsApp integration introduces a few tricky issues: malformed URLs, encoding problems, webhook verification, and storage persistence. By correcting your .env variables and database encoding, you can ensure smooth communication between Chatwoot and WhatsApp. 👉 With this setup, your team can manage all customer conversations from a single inbox — across WhatsApp and other channels.

Last updated on Aug 23, 2025

How to Set Up SMTP on a Self-Hosted Chatwoot Instance

hatwoot is a powerful open-source customer engagement platform that helps businesses provide seamless support through multiple channels including live chat, email, WhatsApp, and social media. When self-hosting Chatwoot, one of the key configurations you need to set up is SMTP (Simple Mail Transfer Protocol). This enables Chatwoot to send and receive emails reliably — ensuring your customers receive timely responses and your support team is notified of updates. In this guide, we’ll walk through the step-by-step process of configuring SMTP on a self-hosted Chatwoot setup. Why Configure SMTP in Chatwoot? By default, Chatwoot doesn’t send emails out of the box. Configuring SMTP provides: - Email notifications for agents and admins when new conversations or replies arrive. - Automated responses to customers via email. - Integration with ticketing workflows, especially if you also connect Chatwoot with tools like Helpdesk or CRM systems. - Professional branding, since emails are sent from your own domain (e.g., support@yourcompany.com). Step 1: Gather Your SMTP Credentials Before you start, you need SMTP details from your email provider or server. For example, if you’re using  cPanel** (yourdomain.com)** or Gmail/Outlook, your SMTP details will look like this: - SMTP Host: mail.yourdomain.com (or smtp.gmail.com, smtp.office365.com, etc.) - Port: 465 (SSL) or 587 (TLS) - Username: Your full email address (e.g., support@yourdomain.com) - Password: Your email account password or app password (recommended). - From Email: support@yourdomain.com - From Name: Your company name (e.g., Nestict Cloud Support). Step 2: Edit Chatwoot Environment File On your server, Chatwoot is usually deployed via Docker or installed directly with Ruby on Rails. You’ll need to edit the environment variables in the .env file. Open the file and add/update these lines: # SMTP Settings MAILER_SENDER_EMAIL=support@yourdomain.com SMTP_ADDRESS=mail.yourdomain.com SMTP_PORT=587 SMTP_DOMAIN=yourdomain.com SMTP_USERNAME=support@yourdomain.com SMTP_PASSWORD=yourpassword SMTP_AUTHENTICATION=plain SMTP_ENABLE_STARTTLS_AUTO=true 👉 Replace values with your actual SMTP credentials. Our Nestict Cloud Email Support – Simplified 24/7 Helpdesk Learn More Step 3: Restart Chatwoot Services After updating the .env file, restart your Chatwoot instance for the changes to take effect. For Docker installations: docker-compose down docker-compose up -d For manual installations: systemctl restart chatwoot.target Step 4: Verify SMTP in Chatwoot 1. Log in to your Chatwoot dashboard. 2. Go to Settings > Email Configuration. 3. Send a test email to confirm the connection. If successful, you’ll see a confirmation message and receive the email in your inbox. Step 5: Configure Inbound Email (Optional) If you want customers to reply to Chatwoot emails and continue conversations via email: 1. Set up a forwarding rule in your email provider (e.g., cPanel, Gmail, Outlook). 2. Forward incoming emails to your Chatwoot inbox (e.g., reply+<account_id>@yourdomain.com). 3. Verify in Chatwoot that emails are being converted into conversations. Troubleshooting Tips - If emails aren’t sending, double-check your SMTP host, port, and authentication method. - For Gmail/Outlook, make sure you enable App Passwords instead of using your main account password. - If using cPanel, ensure SMTP restrictions are not enabled by default. - Use docker-compose logs -f chatwoot to check for SMTP connection errors. Final Thoughts Setting up SMTP in Chatwoot ensures your customer support system is reliable, professional, and responsive. With emails fully integrated, you can confidently manage customer queries, send updates, and maintain smooth communication. At Nestict Infotech, we use Chatwoot to manage customer support across multiple channels. Our branded email support (Nestict Cloud Email Support) ensures that clients always receive prompt responses, whether through live chat, tickets on selfcare.nestict.africa, or emails via support@nestict.com. By following these steps, you’ll have a fully functioning SMTP setup in Chatwoot — enabling you to deliver an excellent customer experience.

Last updated on Aug 23, 2025

Our Nestict Cloud Email Support – Simplified 24/7 Helpdesk

Introduction At Nestict Infotech, we understand that reliable and accessible support is at the heart of excellent service. That’s why we’ve built Nestict Cloud Email Support, a customer-first solution designed to simplify communication and ensure no query goes unanswered. Whether you are reaching us via email, chat, or our self-care ticket system, we make sure your concerns are resolved promptly and professionally. Why Nestict Cloud Email Support? Customers often face challenges when contacting businesses – delayed responses, lost emails, or confusing ticket processes. We’ve solved this problem by integrating a streamlined helpdesk system that keeps your communication consistent across all platforms. Here’s how it works: - 📧 Email Support: Simply write to support@nestict.com and your request is logged instantly into our system. - 💬 Live Chat Assistance: Use our Chatwoot-powered live chat for real-time conversations. If an agent isn’t available immediately, your request is automatically tracked. - 🎫 Ticketing System: Manage, track, and resolve your requests through our self-service portal at https://selfcare.nestict.africa. - 🔄 Conversation Continuity: Every interaction is linked, whether you start from chat, email, or web. Our Support Promise We’re more than just a support desk – we’re your technology partner. By choosing Nestict Cloud Email Support, you benefit from: ✅ 24/7 Availability – Our dedicated team works round-the-clock to serve you. ✅ Simplified Ticket Handling – Faster resolutions through our automated ticket system. ✅ Human-Friendly Communication – Clear, friendly updates without technical jargon. ✅ Multiple Access Channels – Choose email, live chat, self-care portal, or Q&A platforms. Contact Details For your convenience, here’s how you can reach us: 📧 Email: support@nestict.com 🎫 Ticket System: https://selfcare.nestict.africa 📞 Call/WhatsApp (Available 24/7): +254711602911 | +254705042522 Our Office Locations Head Office: The Zonal Cyber, Changamwe Branch: Yatmack Cyber, Safaricom House, Busia 📍 80100 Mombasa, Kenya 📜 Tax #: P051850266J Explore More from Nestict We also provide knowledge-sharing and interactive platforms for our clients and community: - Ask Portal: www.ask.nestict.co.ke - Q&A Platform: www.qa.nestict.com - Official Blog: www.blog.nestict.com With Nestict Cloud Email Support, you are never alone. Our mission is to empower individuals, businesses, and organizations with reliable support and transparent communication. Whenever you need assistance – whether it’s a simple question or a complex technical issue – Nestict Cloud Email Support is just one message away.

Last updated on Aug 23, 2025